fix: 登录问题修复 - session cookie配置 + 前端调试日志 + 力导向图布局优化

- 显式配置 session cookie (HTTPOnly/SameSite/Secure/Name)
- 前端 fetch 增加 credentials: same-origin
- 登录成功后备 cookie 设置
- 服务端 api_login 增加调试日志
- 星图布局基于 mention_count 优化节点间距
- 保留旧数据库数据
This commit is contained in:
root
2026-05-08 12:25:22 +08:00
parent 807242dd78
commit 3839a60459
124 changed files with 22917 additions and 13 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"buildMode":"Debug"}

View File

@ -0,0 +1 @@
export { ChatPage } from "@normalized:N&&&@ohos/chat/src/main/ets/pages/ChatPage&1.0.0";

View File

@ -0,0 +1,488 @@
if (!("finalizeConstruction" in ViewPU.prototype)) {
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface ChatMessageList_Params {
messages?: ChatMessage[];
isThinking?: boolean;
toolCallLog?: string;
scrollController?: Scroller;
}
interface ChatInputBar_Params {
inputText?: string;
isThinking?: boolean;
onSend?: () => void;
}
interface ToolCallLogPanel_Params {
logText?: string;
}
interface ThinkingIndicator_Params {
}
interface ChatMessageBubble_Params {
msg?: ChatMessage;
}
import type { ChatMessage } from '@ohos/common';
export class ChatMessageBubble extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.__msg = new SynchedPropertyNesedObjectPU(params.msg, this, "msg");
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: ChatMessageBubble_Params) {
this.__msg.set(params.msg);
}
updateStateVars(params: ChatMessageBubble_Params) {
this.__msg.set(params.msg);
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__msg.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__msg.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private __msg: SynchedPropertyNesedObjectPU<ChatMessage>;
get msg() {
return this.__msg.get();
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.padding(12);
Column.backgroundColor(this.msg.role === 'user' ? 'rgba(124,77,255,0.15)' : 'rgba(245,245,245,0.1)');
Column.borderRadius(12);
Column.border({
width: 1,
color: this.msg.role === 'user' ? 'rgba(124,77,255,0.3)' : 'rgba(255,255,255,0.1)'
});
Column.backgroundBlurStyle(BlurStyle.Thin);
Column.margin({ left: 8, right: 8, bottom: 8 });
Column.width('100%');
Column.alignItems(HorizontalAlign.Start);
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
// 角色标识
Text.create(this.msg.role === 'user' ? '🧑 你' : '🤖 AI');
// 角色标识
Text.fontSize(11);
// 角色标识
Text.fontColor(this.msg.role === 'user' ? '#7C4DFF' : '#999');
// 角色标识
Text.width('100%');
}, Text);
// 角色标识
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
// 消息内容
Text.create(this.msg.content);
// 消息内容
Text.fontSize(15);
// 消息内容
Text.width('100%');
// 消息内容
Text.margin({ top: 4 });
// 消息内容
Text.fontColor('#FFFFFF');
}, Text);
// 消息内容
Text.pop();
Column.pop();
}
rerender() {
this.updateDirtyElements();
}
}
export class ThinkingIndicator extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: ThinkingIndicator_Params) {
}
updateStateVars(params: ThinkingIndicator_Params) {
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
}
aboutToBeDeleted() {
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Row.create();
Row.padding(12);
Row.backgroundColor('rgba(124,77,255,0.1)');
Row.borderRadius(12);
Row.border({ width: 1, color: 'rgba(124,77,255,0.2)' });
Row.backgroundBlurStyle(BlurStyle.Thin);
Row.margin({ left: 8, right: 8, bottom: 8 });
}, Row);
this.observeComponentCreation2((elmtId, isInitialRender) => {
LoadingProgress.create();
LoadingProgress.width(20);
LoadingProgress.height(20);
LoadingProgress.margin({ right: 8 });
LoadingProgress.color('#7C4DFF');
}, LoadingProgress);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create('AI 思考中...');
Text.fontSize(13);
Text.fontColor('#7C4DFF');
}, Text);
Text.pop();
Row.pop();
}
rerender() {
this.updateDirtyElements();
}
}
export class ToolCallLogPanel extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.__logText = new SynchedPropertySimpleOneWayPU(params.logText, this, "logText");
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: ToolCallLogPanel_Params) {
}
updateStateVars(params: ToolCallLogPanel_Params) {
this.__logText.reset(params.logText);
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__logText.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__logText.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private __logText: SynchedPropertySimpleOneWayPU<string>;
get logText() {
return this.__logText.get();
}
set logText(newValue: string) {
this.__logText.set(newValue);
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(this.logText);
Text.fontSize(10);
Text.fontColor('#FF9800');
Text.backgroundColor('rgba(255,152,0,0.1)');
Text.padding(8);
Text.borderRadius(8);
Text.border({ width: 1, color: 'rgba(255,152,0,0.2)' });
Text.backgroundBlurStyle(BlurStyle.Thin);
Text.margin({ left: 8, right: 8, bottom: 4 });
Text.lineHeight(16);
}, Text);
Text.pop();
}
rerender() {
this.updateDirtyElements();
}
}
export class ChatInputBar extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.__inputText = new SynchedPropertySimpleTwoWayPU(params.inputText, this, "inputText");
this.__isThinking = new SynchedPropertySimpleOneWayPU(params.isThinking, this, "isThinking");
this.onSend = undefined;
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: ChatInputBar_Params) {
if (params.onSend !== undefined) {
this.onSend = params.onSend;
}
}
updateStateVars(params: ChatInputBar_Params) {
this.__isThinking.reset(params.isThinking);
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__inputText.purgeDependencyOnElmtId(rmElmtId);
this.__isThinking.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__inputText.aboutToBeDeleted();
this.__isThinking.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private __inputText: SynchedPropertySimpleTwoWayPU<string>;
get inputText() {
return this.__inputText.get();
}
set inputText(newValue: string) {
this.__inputText.set(newValue);
}
private __isThinking: SynchedPropertySimpleOneWayPU<boolean>;
get isThinking() {
return this.__isThinking.get();
}
set isThinking(newValue: boolean) {
this.__isThinking.set(newValue);
}
private onSend?: () => void;
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Row.create();
Row.width('100%');
Row.padding(8);
Row.backgroundColor('rgba(255,255,255,0.05)');
Row.backgroundBlurStyle(BlurStyle.Regular);
Row.border({
width: 1,
color: 'rgba(124,77,255,0.2)',
style: BorderStyle.Solid
});
}, Row);
this.observeComponentCreation2((elmtId, isInitialRender) => {
TextArea.create({ text: this.inputText, placeholder: '输入消息...' });
TextArea.layoutWeight(1);
TextArea.onChange((v: string) => { this.inputText = v; });
TextArea.height(40);
TextArea.backgroundColor('rgba(255,255,255,0.1)');
TextArea.borderRadius(8);
TextArea.border({ width: 1, color: 'rgba(124,77,255,0.3)' });
}, TextArea);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Button.createWithLabel('发送');
Button.enabled(!this.isThinking);
Button.onClick(() => { this.onSend?.(); });
Button.backgroundColor('#7C4DFF');
Button.borderRadius(8);
}, Button);
Button.pop();
Row.pop();
}
rerender() {
this.updateDirtyElements();
}
}
export class ChatMessageList extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.__messages = new SynchedPropertyObjectOneWayPU(params.messages, this, "messages");
this.__isThinking = new SynchedPropertySimpleOneWayPU(params.isThinking, this, "isThinking");
this.__toolCallLog = new SynchedPropertySimpleOneWayPU(params.toolCallLog, this, "toolCallLog");
this.scrollController = new Scroller();
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: ChatMessageList_Params) {
if (params.scrollController !== undefined) {
this.scrollController = params.scrollController;
}
}
updateStateVars(params: ChatMessageList_Params) {
this.__messages.reset(params.messages);
this.__isThinking.reset(params.isThinking);
this.__toolCallLog.reset(params.toolCallLog);
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__messages.purgeDependencyOnElmtId(rmElmtId);
this.__isThinking.purgeDependencyOnElmtId(rmElmtId);
this.__toolCallLog.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__messages.aboutToBeDeleted();
this.__isThinking.aboutToBeDeleted();
this.__toolCallLog.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private __messages: SynchedPropertySimpleOneWayPU<ChatMessage[]>;
get messages() {
return this.__messages.get();
}
set messages(newValue: ChatMessage[]) {
this.__messages.set(newValue);
}
private __isThinking: SynchedPropertySimpleOneWayPU<boolean>;
get isThinking() {
return this.__isThinking.get();
}
set isThinking(newValue: boolean) {
this.__isThinking.set(newValue);
}
private __toolCallLog: SynchedPropertySimpleOneWayPU<string>;
get toolCallLog() {
return this.__toolCallLog.get();
}
set toolCallLog(newValue: string) {
this.__toolCallLog.set(newValue);
}
private scrollController: Scroller;
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
List.create();
List.width('100%');
List.layoutWeight(1);
List.backgroundColor('rgba(0,0,0,0.1)');
}, List);
this.observeComponentCreation2((elmtId, isInitialRender) => {
ForEach.create();
const forEachItemGenFunction = _item => {
const msg = _item;
{
const itemCreation = (elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
ListItem.create(deepRenderFunction, true);
if (!isInitialRender) {
ListItem.pop();
}
ViewStackProcessor.StopGetAccessRecording();
};
const itemCreation2 = (elmtId, isInitialRender) => {
ListItem.create(deepRenderFunction, true);
};
const deepRenderFunction = (elmtId, isInitialRender) => {
itemCreation(elmtId, isInitialRender);
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new ChatMessageBubble(this, { msg: msg }, undefined, elmtId, () => { }, { page: "features/chat/src/main/ets/components/ChatComponents.ets", line: 141, col: 11 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
msg: msg
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
msg: msg
});
}
}, { name: "ChatMessageBubble" });
}
ListItem.pop();
};
this.observeComponentCreation2(itemCreation2, ListItem);
ListItem.pop();
}
};
this.forEachUpdateFunction(elmtId, this.messages, forEachItemGenFunction);
}, ForEach);
ForEach.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
If.create();
if (this.isThinking) {
this.ifElseBranchUpdateFunction(0, () => {
{
const itemCreation = (elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
ListItem.create(deepRenderFunction, true);
if (!isInitialRender) {
ListItem.pop();
}
ViewStackProcessor.StopGetAccessRecording();
};
const itemCreation2 = (elmtId, isInitialRender) => {
ListItem.create(deepRenderFunction, true);
};
const deepRenderFunction = (elmtId, isInitialRender) => {
itemCreation(elmtId, isInitialRender);
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new ThinkingIndicator(this, {}, undefined, elmtId, () => { }, { page: "features/chat/src/main/ets/components/ChatComponents.ets", line: 147, col: 11 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {});
}
}, { name: "ThinkingIndicator" });
}
ListItem.pop();
};
this.observeComponentCreation2(itemCreation2, ListItem);
ListItem.pop();
}
});
}
else {
this.ifElseBranchUpdateFunction(1, () => {
});
}
}, If);
If.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
If.create();
if (this.toolCallLog && !this.isThinking) {
this.ifElseBranchUpdateFunction(0, () => {
{
const itemCreation = (elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
ListItem.create(deepRenderFunction, true);
if (!isInitialRender) {
ListItem.pop();
}
ViewStackProcessor.StopGetAccessRecording();
};
const itemCreation2 = (elmtId, isInitialRender) => {
ListItem.create(deepRenderFunction, true);
};
const deepRenderFunction = (elmtId, isInitialRender) => {
itemCreation(elmtId, isInitialRender);
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new ToolCallLogPanel(this, { logText: this.toolCallLog }, undefined, elmtId, () => { }, { page: "features/chat/src/main/ets/components/ChatComponents.ets", line: 153, col: 11 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
logText: this.toolCallLog
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
logText: this.toolCallLog
});
}
}, { name: "ToolCallLogPanel" });
}
ListItem.pop();
};
this.observeComponentCreation2(itemCreation2, ListItem);
ListItem.pop();
}
});
}
else {
this.ifElseBranchUpdateFunction(1, () => {
});
}
}, If);
If.pop();
List.pop();
}
rerender() {
this.updateDirtyElements();
}
}

View File

@ -0,0 +1,219 @@
if (!("finalizeConstruction" in ViewPU.prototype)) {
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface ChatPage_Params {
messages?: ChatMessage[];
inputText?: string;
db?: GraphDatabase;
toolCallLog?: string;
isThinking?: boolean;
agentService?: AIAgentService;
}
import { GraphMemoryService, AIAgentService, Logger } from "@normalized:N&&&@ohos/common/Index&1.0.0";
import type { GraphDatabase, ChatMessage, AgentResponse } from "@normalized:N&&&@ohos/common/Index&1.0.0";
import { ChatMessageList, ChatInputBar } from "@normalized:N&&&@ohos/chat/src/main/ets/components/ChatComponents&1.0.0";
export class ChatPage extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.__messages = new ObservedPropertyObjectPU([], this, "messages");
this.__inputText = new ObservedPropertySimplePU('', this, "inputText");
this.__db = new SynchedPropertyObjectOneWayPU(params.db, this, "db");
this.__toolCallLog = new ObservedPropertySimplePU('', this, "toolCallLog");
this.__isThinking = new ObservedPropertySimplePU(false, this, "isThinking");
this.agentService = undefined;
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: ChatPage_Params) {
if (params.messages !== undefined) {
this.messages = params.messages;
}
if (params.inputText !== undefined) {
this.inputText = params.inputText;
}
if (params.toolCallLog !== undefined) {
this.toolCallLog = params.toolCallLog;
}
if (params.isThinking !== undefined) {
this.isThinking = params.isThinking;
}
if (params.agentService !== undefined) {
this.agentService = params.agentService;
}
}
updateStateVars(params: ChatPage_Params) {
this.__db.reset(params.db);
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__messages.purgeDependencyOnElmtId(rmElmtId);
this.__inputText.purgeDependencyOnElmtId(rmElmtId);
this.__db.purgeDependencyOnElmtId(rmElmtId);
this.__toolCallLog.purgeDependencyOnElmtId(rmElmtId);
this.__isThinking.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__messages.aboutToBeDeleted();
this.__inputText.aboutToBeDeleted();
this.__db.aboutToBeDeleted();
this.__toolCallLog.aboutToBeDeleted();
this.__isThinking.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private __messages: ObservedPropertyObjectPU<ChatMessage[]>;
get messages() {
return this.__messages.get();
}
set messages(newValue: ChatMessage[]) {
this.__messages.set(newValue);
}
private __inputText: ObservedPropertySimplePU<string>;
get inputText() {
return this.__inputText.get();
}
set inputText(newValue: string) {
this.__inputText.set(newValue);
}
private __db: SynchedPropertySimpleOneWayPU<GraphDatabase>;
get db() {
return this.__db.get();
}
set db(newValue: GraphDatabase) {
this.__db.set(newValue);
}
private __toolCallLog: ObservedPropertySimplePU<string>;
get toolCallLog() {
return this.__toolCallLog.get();
}
set toolCallLog(newValue: string) {
this.__toolCallLog.set(newValue);
}
private __isThinking: ObservedPropertySimplePU<boolean>;
get isThinking() {
return this.__isThinking.get();
}
set isThinking(newValue: boolean) {
this.__isThinking.set(newValue);
}
private agentService?: AIAgentService;
async aboutToAppear() {
// 初始化图记忆服务和 Agent
const memoryService = new GraphMemoryService(this.db);
this.agentService = new AIAgentService(memoryService, getContext(this));
// 加载历史消息(兼容旧数据:无 session_id 时加载全部)
const rawHistory = await this.db.getChatHistory(50, this.agentService.getSessionId());
if (rawHistory.length === 0) {
// 新 session尝试加载旧消息
const legacyHistory = await this.db.getChatHistory(50);
this.messages = legacyHistory.map(m => {
const msg: ChatMessage = { role: m.role, content: m.content };
return msg;
});
}
else {
this.messages = rawHistory.map(m => {
const msg: ChatMessage = { role: m.role, content: m.content };
return msg;
});
}
}
async sendMessage() {
if (!this.inputText.trim() || !this.agentService)
return;
const userMessage: string = this.inputText;
this.inputText = '';
// 添加用户消息
await this.db.saveChatMessage('user', userMessage, '', this.agentService.getSessionId());
this.messages = [...this.messages, { role: 'user', content: userMessage }];
// 显示 loading
this.isThinking = true;
this.toolCallLog = '';
try {
// 通过 Agent 发送消息
const agentResponse: AgentResponse = await this.agentService.sendMessage(userMessage);
// 记录工具调用日志
if (agentResponse.toolCalls.length > 0) {
const logs: string[] = agentResponse.toolCalls.map(tc => `🛠 ${tc.name}: ${tc.message}`);
this.toolCallLog = logs.join('\n');
}
// 保存并显示 AI 回复
await this.db.saveChatMessage('assistant', agentResponse.content, this.toolCallLog, this.agentService.getSessionId());
this.messages = [...this.messages, { role: 'assistant', content: agentResponse.content }];
}
catch (err) {
Logger.error('Agent request failed: ' + JSON.stringify(err));
const errMsg = (err as Error).message || JSON.stringify(err);
this.messages = [...this.messages, { role: 'assistant', content: `⚠️ 请求失败: ${errMsg}` }];
}
finally {
this.isThinking = false;
}
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.width('100%');
Column.height('100%');
Column.backgroundColor('rgba(26,27,46,0.95)');
}, Column);
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new ChatMessageList(this, {
messages: this.messages,
isThinking: this.isThinking,
toolCallLog: this.toolCallLog
}, undefined, elmtId, () => { }, { page: "features/chat/src/main/ets/pages/ChatPage.ets", line: 73, col: 13 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
messages: this.messages,
isThinking: this.isThinking,
toolCallLog: this.toolCallLog
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
messages: this.messages,
isThinking: this.isThinking,
toolCallLog: this.toolCallLog
});
}
}, { name: "ChatMessageList" });
}
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new ChatInputBar(this, {
inputText: this.__inputText,
isThinking: this.isThinking,
onSend: (): void => { this.sendMessage(); }
}, undefined, elmtId, () => { }, { page: "features/chat/src/main/ets/pages/ChatPage.ets", line: 79, col: 13 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
inputText: this.inputText,
isThinking: this.isThinking,
onSend: (): void => { this.sendMessage(); }
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
isThinking: this.isThinking
});
}
}, { name: "ChatInputBar" });
}
Column.pop();
}
rerender() {
this.updateDirtyElements();
}
}

View File

@ -0,0 +1,16 @@
export { defaultLogger } from "@normalized:N&&&@ohos/common/src/main/ets/util/Logger&1.0.0";
export { defaultLogger as Logger } from "@normalized:N&&&@ohos/common/src/main/ets/util/Logger&1.0.0";
export { BreakpointType, WidthBreakpoint } from "@normalized:N&&&@ohos/common/src/main/ets/util/BreakpointSystem&1.0.0";
export type { BreakpointTypes } from "@normalized:N&&&@ohos/common/src/main/ets/util/BreakpointSystem&1.0.0";
export { PageContext } from "@normalized:N&&&@ohos/common/src/main/ets/routermanager/PageContext&1.0.0";
export type { RouterParam, IPageContext } from "@normalized:N&&&@ohos/common/src/main/ets/routermanager/PageContext&1.0.0";
export { Constants as TrulyMEMConstants } from "@normalized:N&&&@ohos/common/src/main/ets/constant/TrulyMEMConstants&1.0.0";
export { GraphDatabase } from "@normalized:N&&&@ohos/common/src/main/ets/model/GraphDatabase&1.0.0";
export type { RecallEntity, TimeRangeParams } from "@normalized:N&&&@ohos/common/src/main/ets/model/GraphDatabase&1.0.0";
export { GraphMemoryService } from "@normalized:N&&&@ohos/common/src/main/ets/service/GraphMemoryService&1.0.0";
export type { ConnectionItem, NodeDetailInfo } from "@normalized:N&&&@ohos/common/src/main/ets/service/GraphMemoryService&1.0.0";
export { AIAgentService } from "@normalized:N&&&@ohos/common/src/main/ets/service/AIAgentService&1.0.0";
export type { ChatMessage, AgentResponse } from "@normalized:N&&&@ohos/common/src/main/ets/service/AIAgentService&1.0.0";
export { BaseViewModel } from "@normalized:N&&&@ohos/common/src/main/ets/viewmodel/BaseViewModel&1.0.0";
export type { VMEvent } from "@normalized:N&&&@ohos/common/src/main/ets/viewmodel/BaseViewModel&1.0.0";
export { ImmersiveTabNavigation } from "@normalized:N&&&@ohos/common/src/main/ets/component/ImmersiveTabNavigation&1.0.0";

View File

@ -0,0 +1,236 @@
if (!("finalizeConstruction" in ViewPU.prototype)) {
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface ImmersiveTabNavigation_Params {
currentIndex?: number;
contentBuilder?: () => void;
onTabChange?: (index: number) => void;
windowFocused?: boolean;
bottomAvoidHeight?: number;
}
import { defaultLogger } from "@normalized:N&&&@ohos/common/src/main/ets/util/Logger&1.0.0";
import window from "@ohos:window";
import type { BusinessError } from "@ohos:base";
const THEME_COLOR = '#7C4DFF';
export class ImmersiveTabNavigation extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.__currentIndex = new ObservedPropertySimplePU(0, this, "currentIndex");
this.contentBuilder = undefined;
this.onTabChange = undefined;
this.windowFocused = true;
this.bottomAvoidHeight = 0;
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: ImmersiveTabNavigation_Params) {
if (params.currentIndex !== undefined) {
this.currentIndex = params.currentIndex;
}
if (params.contentBuilder !== undefined) {
this.contentBuilder = params.contentBuilder;
}
if (params.onTabChange !== undefined) {
this.onTabChange = params.onTabChange;
}
if (params.windowFocused !== undefined) {
this.windowFocused = params.windowFocused;
}
if (params.bottomAvoidHeight !== undefined) {
this.bottomAvoidHeight = params.bottomAvoidHeight;
}
}
updateStateVars(params: ImmersiveTabNavigation_Params) {
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__currentIndex.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__currentIndex.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private __currentIndex: ObservedPropertySimplePU<number>;
get currentIndex() {
return this.__currentIndex.get();
}
set currentIndex(newValue: number) {
this.__currentIndex.set(newValue);
}
private __contentBuilder;
private onTabChange?: (index: number) => void;
private windowFocused: boolean;
private bottomAvoidHeight: number;
aboutToAppear() {
const mainWindow = AppStorage.get<window.Window>('main_window');
if (mainWindow) {
try {
const avoidArea = mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
this.bottomAvoidHeight = avoidArea.bottomRect.height || 0;
}
catch (e) {
defaultLogger.error('Failed to get avoid area: ' + (e as BusinessError).message);
}
}
}
triggerTabSwitchFeedback(index: number) {
this.currentIndex = index;
AppStorage.setOrCreate('global_theme_color', THEME_COLOR);
this.onTabChange?.(index);
}
tabBarBuilder(index: number, icon: string, label: string, parent = null) {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.width('100%');
Column.height(56);
Column.justifyContent(FlexAlign.Center);
Column.alignItems(HorizontalAlign.Center);
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
If.create();
if (this.currentIndex === index && this.windowFocused) {
this.ifElseBranchUpdateFunction(0, () => {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Circle.create();
Circle.width(32);
Circle.height(32);
Circle.backgroundColor(`${THEME_COLOR}33`);
Circle.blur(8);
Circle.position({ x: '50%', y: '50%' });
Circle.translate({ x: '-50%', y: '-50%' });
}, Circle);
});
}
else {
this.ifElseBranchUpdateFunction(1, () => {
});
}
}, If);
If.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(icon);
Text.fontSize(20);
Text.opacity(this.currentIndex === index ? 1 : 0.5);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(label);
Text.fontSize(10);
Text.fontColor(this.currentIndex === index ? THEME_COLOR : '#999');
Text.fontWeight(this.currentIndex === index ? FontWeight.Bold : FontWeight.Normal);
}, Text);
Text.pop();
Column.pop();
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Stack.create();
Stack.width('100%');
Stack.height('100%');
Stack.backgroundColor('#00000000');
}, Stack);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.width('100%');
Column.height('100%');
}, Column);
this.contentBuilder.bind(this)();
Column.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.width('92%');
Column.height(72);
Column.alignSelf(ItemAlign.Center);
Column.position({ y: `calc(100% - ${this.bottomAvoidHeight > 0 ? this.bottomAvoidHeight : 16}px - 72px)` });
Column.borderRadius(24);
Column.shadow({
radius: 20,
offsetY: -4,
color: 'rgba(0,0,0,0.15)'
});
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Stack.create();
Stack.width('100%');
Stack.height('100%');
}, Stack);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.width('100%');
Column.height('100%');
Column.backgroundBlurStyle(BlurStyle.Regular);
Column.borderRadius(24);
}, Column);
Column.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.width('100%');
Column.height('100%');
Column.backgroundColor(`${THEME_COLOR}0D`);
Column.borderRadius(24);
}, Column);
Column.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.width('100%');
Column.height('100%');
Column.linearGradient({
angle: 180,
colors: [['rgba(255,255,255,0.15)', 0.0], ['rgba(255,255,255,0.05)', 1.0]]
});
Column.borderRadius(24);
}, Column);
Column.pop();
Stack.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Tabs.create({ index: this.currentIndex });
Tabs.width('100%');
Tabs.height(64);
Tabs.barPosition(BarPosition.End);
Tabs.onChange((index: number) => {
this.triggerTabSwitchFeedback(index);
});
}, Tabs);
this.observeComponentCreation2((elmtId, isInitialRender) => {
TabContent.create(() => {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Blank.create();
}, Blank);
Blank.pop();
Column.pop();
});
TabContent.tabBar({ builder: () => {
this.tabBarBuilder.call(this, 0, '🌌', 'TrulyMEM');
} });
}, TabContent);
TabContent.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
TabContent.create(() => {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Blank.create();
}, Blank);
Blank.pop();
Column.pop();
});
TabContent.tabBar({ builder: () => {
this.tabBarBuilder.call(this, 1, '⚙', '设置');
} });
}, TabContent);
TabContent.pop();
Tabs.pop();
Column.pop();
Stack.pop();
}
rerender() {
this.updateDirtyElements();
}
}

View File

@ -0,0 +1,40 @@
export class Constants {
static readonly DB_NAME: string = 'trulymem.db';
static readonly CONFIG_PREF_NAME: string = 'trulymem_config';
static readonly DEFAULT_BASE_URL: string = 'https://api.deepseek.com';
static readonly DEFAULT_MODEL: string = 'deepseek-chat';
static readonly SECURITY_LEVEL: number = 1; // S1
// Table names
static readonly TABLE_NODES: string = 'nodes';
static readonly TABLE_RELATIONS: string = 'relations';
static readonly TABLE_CHAT: string = 'chat_records';
// SQL definitions
static readonly SQL_CREATE_NODES: string = `
CREATE TABLE IF NOT EXISTS nodes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
type TEXT DEFAULT 'concept',
mentions INTEGER DEFAULT 1,
created_at TEXT DEFAULT (datetime('now','localtime')),
updated_at TEXT DEFAULT (datetime('now','localtime'))
)`;
static readonly SQL_CREATE_RELATIONS: string = `
CREATE TABLE IF NOT EXISTS relations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
subject_id INTEGER NOT NULL,
relation TEXT NOT NULL,
object_id INTEGER NOT NULL,
weight REAL DEFAULT 1.0,
created_at TEXT DEFAULT (datetime('now','localtime')),
FOREIGN KEY (subject_id) REFERENCES nodes(id) ON DELETE CASCADE,
FOREIGN KEY (object_id) REFERENCES nodes(id) ON DELETE CASCADE
)`;
static readonly SQL_CREATE_CHAT: string = `
CREATE TABLE IF NOT EXISTS chat_records (
id INTEGER PRIMARY KEY AUTOINCREMENT,
role TEXT NOT NULL,
content TEXT NOT NULL,
tools TEXT,
created_at TEXT DEFAULT (datetime('now','localtime'))
)`;
}

View File

@ -0,0 +1,53 @@
import { defaultLogger } from "@normalized:N&&&@ohos/common/src/main/ets/util/Logger&1.0.0";
import type { BusinessError } from "@ohos:base";
export interface RouterParam {
routerName: string;
param?: object;
}
export interface IPageContext {
openPage(data: RouterParam, animated?: boolean): void;
popPage(animated?: boolean): void;
replacePage(data: RouterParam, animated?: boolean): void;
}
export class PageContext implements IPageContext {
private readonly pathStack: NavPathStack;
constructor() {
this.pathStack = new NavPathStack();
}
public get navPathStack(): NavPathStack {
return this.pathStack;
}
public replacePage(data: RouterParam, animated: boolean = true): void {
try {
this.pathStack.replacePath({ name: data.routerName, param: data.param }, animated);
}
catch (err) {
const errMsg = (err as BusinessError).message || JSON.stringify(err);
defaultLogger.error('replacePage: ' + data.routerName + ' failed. ' + errMsg);
}
}
public openPage(data: RouterParam, animated: boolean = true): void {
try {
this.pathStack.pushPath({ name: data.routerName, param: data.param }, animated);
}
catch (err) {
const errMsg = (err as BusinessError).message || JSON.stringify(err);
defaultLogger.error('openPage: ' + data.routerName + ' failed. ' + errMsg);
}
}
public popPage(animated: boolean = true): void {
try {
this.pathStack.pop(animated);
}
catch (err) {
const errMsg = (err as BusinessError).message || JSON.stringify(err);
defaultLogger.error('popPage failed. ' + errMsg);
}
}
public popPageByIndex(index: number, animated: boolean = true): void {
this.pathStack.popToIndex(index, animated);
}
public clear(animated: boolean = true): void {
this.pathStack.clear(animated);
}
}

View File

@ -0,0 +1,785 @@
import http from "@ohos:net.http";
import dataPreferences from "@ohos:data.preferences";
import type common from "@ohos:app.ability.common";
import { defaultLogger } from "@normalized:N&&&@ohos/common/src/main/ets/util/Logger&1.0.0";
import type { GraphMemoryService, EntityInfo, RelationInfo, MemoryRecallParams, MemoryCommitParams, MemoryPurgeParams, PurgeCriteriaParams, NewRelationParams, PersonaUpdateParams, TaskCreateParams, TaskSetStateParams, TaskDeleteParams, TaskLinkInfoParams, TaskArchiveParams, TripletInput, PersonaQueryResult, MemoryRecallResult } from './GraphMemoryService';
import type { TimeRangeParams } from '../model/GraphDatabase';
export interface ChatMessage {
role: string;
content: string;
}
export interface AgentResponse {
content: string;
toolCalls: ToolCallResult[];
}
export interface ToolCallResult {
name: string;
success: boolean;
message: string;
}
// ========= 工具定义类型 =========
// Concrete interface for tool property definitions (replaces Record<string, T>)
interface ToolPropertiesDefinition {
days?: ToolParamProperty;
queryIntent?: ToolParamProperty;
seedEntities?: ToolParamProperty;
depth?: ToolParamProperty;
timeRange?: ToolParamProperty;
sessionFilter?: ToolParamProperty;
triplets?: ToolParamProperty;
entityTypes?: ToolParamProperty;
sessionId?: ToolParamProperty;
turnId?: ToolParamProperty;
criteria?: ToolParamProperty;
mode?: ToolParamProperty;
newRelation?: ToolParamProperty;
tone?: ToolParamProperty;
style?: ToolParamProperty;
personality?: ToolParamProperty;
catchphrase?: ToolParamProperty;
background?: ToolParamProperty;
taskId?: ToolParamProperty;
description?: ToolParamProperty;
infoNodes?: ToolParamProperty;
state?: ToolParamProperty;
deleteInfoNodes?: ToolParamProperty;
infoNodeNames?: ToolParamProperty;
summary?: ToolParamProperty;
limit?: ToolParamProperty;
stateFilter?: ToolParamProperty;
subject?: ToolParamProperty;
relation?: ToolParamProperty;
object?: ToolParamProperty;
confidence?: ToolParamProperty;
subjectContains?: ToolParamProperty;
relationType?: ToolParamProperty;
targetContains?: ToolParamProperty;
target?: ToolParamProperty;
dryRun?: ToolParamProperty;
keyword?: ToolParamProperty;
attribute?: ToolParamProperty;
sourceType?: ToolParamProperty;
targetType?: ToolParamProperty;
sourceHasStatus?: ToolParamProperty;
}
interface ToolParamProperty {
type: string;
description: string;
items?: ToolParamProperty;
properties?: ToolPropertiesDefinition;
required?: string[];
enum?: string[];
}
interface ToolParamDecl {
type: string;
properties: ToolPropertiesDefinition;
required?: string[];
}
interface ToolFunctionDecl {
name: string;
description: string;
parameters: ToolParamDecl;
}
interface ToolFunctionDef {
type: string;
function: ToolFunctionDecl;
}
// ========= API 请求/响应结构 =========
interface ApiRequestMessage {
role: string;
content: string;
}
interface ApiRequest {
model: string;
messages: ApiRequestMessage[];
tools?: ToolFunctionDef[];
tool_choice?: string;
}
interface ApiToolCall {
id: string;
type: string;
function: ToolFunctionCall;
}
interface ToolFunctionCall {
name: string;
arguments: string;
}
interface ApiChoiceMessage {
content?: string;
tool_calls?: ApiToolCall[];
}
interface ApiChoice {
message: ApiChoiceMessage;
}
interface ApiResponse {
choices: ApiChoice[];
}
// ========= 内部结果类型 =========
interface ExecuteToolResult {
name: string;
success: boolean;
message: string;
}
interface BuildContextBlockParams {
persona: Record<string, string>;
found: boolean;
entities: EntityInfo[];
relations: RelationInfo[];
message: string;
}
// ========= 服务方法参数类型 =========
// ========= 工具定义辅助函数 =========
function makeStringProp(description: string): ToolParamProperty {
const result: ToolParamProperty = { type: 'string', description: description };
return result;
}
function makeIntegerProp(description: string): ToolParamProperty {
const result: ToolParamProperty = { type: 'integer', description: description };
return result;
}
function makeObjectProp(description: string, props: ToolPropertiesDefinition, required?: string[]): ToolParamProperty {
const param: ToolParamProperty = { type: 'object', description: description };
param.properties = props;
if (required && required.length > 0) {
param.required = required;
}
return param;
}
function makeArrayProp(description: string, item: ToolParamProperty): ToolParamProperty {
const result: ToolParamProperty = { type: 'array', description: description, items: item };
return result;
}
function makeBoolProp(description: string): ToolParamProperty {
const result: ToolParamProperty = { type: 'boolean', description: description };
return result;
}
function makeNumberProp(description: string): ToolParamProperty {
const result: ToolParamProperty = { type: 'number', description: description };
return result;
}
function makeEnumProp(description: string, enumValues: string[]): ToolParamProperty {
const result: ToolParamProperty = { type: 'string', description: description, enum: enumValues };
return result;
}
function makeToolDef(name: string, description: string, properties: ToolPropertiesDefinition, required?: string[]): ToolFunctionDef {
const params: ToolParamDecl = { type: 'object', properties: properties };
const func: ToolFunctionDecl = { name: name, description: description, parameters: params };
const tool: ToolFunctionDef = { type: 'function', function: func };
if (required && required.length > 0) {
tool.function.parameters.required = required;
}
return tool;
}
/**
* 系统提示词 — AI 人设 + 图记忆使用说明
*/
function buildSystemPrompt(personaContext: string): string {
return `你是 TrulyMEMTrue Memory——一个拥有真实记忆的 AI 助手。
## 核心身份
${personaContext || '你是一个帮助用户记录和回忆信息的助手。你的核心能力是基于图数据库的记忆系统。'}
## ⚠️ 内部执行顺序(不得向用户输出)
以下步骤是内部流程,绝对不要在你的回复中提及或输出。应当仅通过工具调用悄悄完成,回复时直接给出自然的对话内容。
1. memory_recall → 查询人设图
2. task_query → 查询工作记忆链/最近任务
3. 处理对话内容 + 思考回复
4. memory_commit → 写入本轮关键信息到图数据库
5. task_archive → 归档已完成的旧任务
6. 条件: 本轮调用 ≥5 次查询类工具 → context_rewrite 压缩工具 JSON
## 三元组规范
使用 memory_commit 时subject/relation/object 每个字段必须是一个短关键字1~5个字不能是完整句子。
## 任务信息节点规范
- info_nodes 只能包含该任务专属的具体信息节点,严禁关联"用户"、"AI"、"系统"等全局通用实体
- 全局实体的信息直接用独立关系记录,不需要通过 Task 中转
## 可用工具
- memory_recall(queryIntent, seedEntities?, depth?, timeRange?, sessionFilter?): 检索记忆
- memory_commit(triplets, entityTypes?, sessionId?, turnId?): 写入记忆
- memory_purge(criteria, mode, newRelation?): 删除/修正记忆
- memory_introspect(sessionId?): 查看记忆状态统计
- memory_archive(days?): 归档旧记忆
- memory_cleanup(dryRun?): 清理已删除数据
- memory_query_archived(days?, keyword?): 查询已归档记忆
- context_rewrite(summary): 压缩工具调用上下文
- persona_update(tone?, style?, personality?, catchphrase?, background?): 更新人设
- persona_remove(attribute): 删除单条人设属性
- persona_clear(): 清除人设
- task_create(taskId, description, infoNodes?): 创建任务
- task_set_state(taskId, state): 设置任务状态
- task_delete(taskId, deleteInfoNodes?): 删除任务
- task_link_info(taskId, infoNodeNames): 关联信息节点
- task_archive(taskId, summary?): 归档任务
- task_query(limit?, stateFilter?): 查询任务列表
## 工具调用规则
1. ⚠️ 在完成所有工具调用之前,绝对不要输出任何文字。先默默调用工具,等所有结果返回后再输出一次完整的回复。
2. 每轮对话必须按顺序执行步骤1查询人设 → 步骤2查询工作记忆链 → 步骤3处理请求
3. context_rewrite 必须单独调用,不能和其他工具在同一轮一起调!
4. 工具调用 ≥5 次后应使用 context_rewrite 压缩上下文
## 写入规则
用户明确表达以下信息时必须写入记忆:
- 偏好、兴趣
- 个人信息(工作、项目、学习)
- 计划安排
- 当前状态
- 结论性事实
推理得到的信息可以写入但需标注 [推测]。`;
}
// ========= 工具定义 =========
// Pre-typed property dictionaries for tool definitions
const recallTimeRangeDict: ToolPropertiesDefinition = { days: makeIntegerProp('最近N天') };
const tripletPropsDict: ToolPropertiesDefinition = {
subject: makeStringProp('主体'),
relation: makeStringProp('关系'),
object: makeStringProp('客体'),
confidence: makeNumberProp('置信度')
};
const purgeCriteriaDict: ToolPropertiesDefinition = {
subjectContains: makeStringProp('源实体名包含(模糊匹配)'),
relationType: makeStringProp('关系类型'),
targetContains: makeStringProp('目标实体名包含(模糊匹配)'),
sessionId: makeStringProp('会话ID过滤'),
sourceType: makeStringProp('源实体类型过滤(如 TaskNode'),
targetType: makeStringProp('目标实体类型过滤'),
sourceHasStatus: makeStringProp('源实体状态过滤(如 archived')
};
const newRelDict: ToolPropertiesDefinition = {
relation: makeStringProp(''),
target: makeStringProp('')
};
const EMPTY_PROPS: ToolPropertiesDefinition = {};
const recallProps: ToolPropertiesDefinition = {
queryIntent: makeStringProp('查询意图,支持逗号分隔多个关键词'),
seedEntities: makeArrayProp('种子实体(可选)', makeStringProp('')),
depth: makeIntegerProp('搜索深度默认2'),
timeRange: makeObjectProp('时间范围(可选)', recallTimeRangeDict),
sessionFilter: makeStringProp('会话ID过滤可选')
};
const commitProps: ToolPropertiesDefinition = {
triplets: makeArrayProp('三元组列表', makeObjectProp('', tripletPropsDict, ['subject', 'relation', 'object'])),
entityTypes: makeObjectProp('实体类型映射(可选)', EMPTY_PROPS),
sessionId: makeStringProp('会话ID可选'),
turnId: makeIntegerProp('轮次ID可选')
};
const purgeProps: ToolPropertiesDefinition = {
criteria: makeObjectProp('删除条件', purgeCriteriaDict),
mode: makeEnumProp('删除模式soft逻辑删除, hard物理删除, supersede纠错替代', ['soft', 'hard', 'supersede']),
newRelation: makeObjectProp('替代关系supersede模式用', newRelDict)
};
const personaProps: ToolPropertiesDefinition = {
tone: makeStringProp('语气'),
style: makeStringProp('风格'),
personality: makeStringProp('性格'),
catchphrase: makeStringProp('口头禅'),
background: makeStringProp('背景')
};
const createProps: ToolPropertiesDefinition = {
taskId: makeStringProp('任务ID'),
description: makeStringProp('任务描述'),
infoNodes: makeArrayProp('关联的信息节点名称列表', makeStringProp(''))
};
const setStateProps: ToolPropertiesDefinition = {
taskId: makeStringProp('任务ID'),
state: makeEnumProp('任务状态', ['进行中', '已完成', '已暂停', '已取消'])
};
const deleteProps: ToolPropertiesDefinition = {
taskId: makeStringProp('任务ID'),
deleteInfoNodes: makeBoolProp('是否删除关联的信息节点')
};
const linkInfoProps: ToolPropertiesDefinition = {
taskId: makeStringProp('任务ID'),
infoNodeNames: makeArrayProp('信息节点名称列表', makeStringProp(''))
};
const archiveProps: ToolPropertiesDefinition = {
taskId: makeStringProp('任务ID'),
summary: makeStringProp('归档摘要')
};
const queryProps: ToolPropertiesDefinition = {
limit: makeIntegerProp('返回数量默认10'),
stateFilter: makeStringProp('状态过滤: 进行中/已完成/已暂停/已取消/archived')
};
const introspectProps: ToolPropertiesDefinition = {
sessionId: makeStringProp('会话ID可选')
};
const archiveProps2: ToolPropertiesDefinition = {
days: makeIntegerProp('归档天数默认30')
};
const cleanupProps: ToolPropertiesDefinition = {
dryRun: makeBoolProp('仅预览不删除')
};
const queryArchivedProps: ToolPropertiesDefinition = {
days: makeIntegerProp('最近N天内的归档记录'),
keyword: makeStringProp('关键词过滤')
};
const contextRewriteProps: ToolPropertiesDefinition = {
summary: makeStringProp('压缩后的摘要文本,必须包含工具调用元信息')
};
const personaRemoveProps: ToolPropertiesDefinition = {
attribute: makeStringProp('要删除的属性名(如:扮演角色、说话风格)')
};
const TOOLS_DEFINITION: ToolFunctionDef[] = [
makeToolDef('memory_recall', '检索记忆。支持关键词、时间范围、会话过滤。返回相关实体和关系。\n\n【⚠ 强制执行顺序 - 每轮必须严格遵守】\n1. 步骤1必须首先执行: 查询人设图\n2. 步骤2必须第二步执行: 查询工作记忆链\n【重要】跳过步骤1或步骤2将导致系统错误', recallProps, ['queryIntent']),
makeToolDef('memory_commit', '写入记忆。将三元组写入图数据库,支持批量写入。\n\n【重要】写入原则:\n- 用户明确表达的信息 → 必须写入\n- AI推理得到的信息 → 可以写入,但需标注[推测]\n- 避免写入冗余或无意义的信息', commitProps, ['triplets']),
makeToolDef('memory_purge', '删除或修正记忆。支持条件删除和纠错替代。\n\n【使用场景】\n- 纠错替代修正错误信息\n- 删除特定类型的节点关系\n- 删除残留在已归档任务上的状态关系\n\n【重要】\n- 优先使用 supersede 模式修正错误\n- 软删除不会物理删除数据', purgeProps, ['criteria', 'mode']),
makeToolDef('memory_introspect', '查看记忆状态。返回实体数量、关系数量、热点实体。', introspectProps),
makeToolDef('memory_archive', '归档旧记忆。将N天前的非活跃关系标记为归档状态。', archiveProps2, ['days']),
makeToolDef('memory_cleanup', '清理无效数据。物理删除已删除状态超过90天的关系和孤立节点。', cleanupProps),
makeToolDef('memory_query_archived', '查询已归档的记忆。\n\n【使用场景】\n- 想了解之前归档过哪些记忆\n- 按关键词搜索归档内容\n- 按时间范围查看最近归档的历史\n\n【注意】\n- 只返回 status=archived 的原始关系记录\n- days 和 keyword 可以单独使用或组合使用', queryArchivedProps),
makeToolDef('context_rewrite', '压缩本轮对话的工具调用上下文。将冗长的JSON工具结果提炼为简洁摘要。\n\n【使用场景】\n- 本轮已执行 ≥5 次查询类工具调用\n- 【⚠️ 强制要求】context_rewrite 必须单独调用,不能和其他工具在同一轮一起调!', contextRewriteProps, ['summary']),
makeToolDef('persona_update', '更新AI人设属性语气、风格、性格等。', personaProps),
makeToolDef('persona_remove', '删除单条人设属性。保留其他人设不变。', personaRemoveProps, ['attribute']),
makeToolDef('persona_clear', '清除所有人设信息。', EMPTY_PROPS),
makeToolDef('task_create', '创建新的工作记忆任务节点。\n\n【重要】info_nodes 只能包含该任务专属的具体信息节点(如\"成语接龙_当前成语\"**严禁关联\"用户\"、\"AI\"、\"系统\"等全局通用实体**——这些实体不应通过任务中转。', createProps, ['taskId', 'description']),
makeToolDef('task_set_state', '设置任务状态。', setStateProps, ['taskId', 'state']),
makeToolDef('task_delete', '删除任务节点。', deleteProps, ['taskId']),
makeToolDef('task_link_info', '关联信息节点到任务。\n\n【重要】info_node_names只能放任务专属的具体信息节点如\"成语接龙_当前成语\"**严禁放\"用户\"、\"AI\"、\"系统\"等全局通用实体**——这些实体不应通过任务中转。', linkInfoProps, ['taskId', 'infoNodeNames']),
makeToolDef('task_archive', '归档已完成/过期的任务。将任务状态设为 archived同时写入完成摘要到图数据库。\n\n【使用场景】\n1. 话题转变时归档旧任务\n2. 已完成的任务及时归档\n3. 长时间无更新的任务归档\n\n【注意】优先使用 task_archive 替代 task_set_state(state=archived),因为它会自动写入完成摘要。', archiveProps, ['taskId']),
makeToolDef('task_query', '查询最近的任务列表。按更新时间倒序排列。新对话开始时优先使用此工具获取所有进展中的任务,避免重复创建。', queryProps)
];
// ========= 工具名称映射 =========
// Types for executeTool generic args
type ToolStateArg = '进行中' | '已完成' | '已暂停' | '已取消';
type ToolHandlerName = 'memoryRecal' | 'memoryCommit' | 'memoryPurge' | 'memoryIntrospect' | 'memoryArchive' | 'memoryCleanup' | 'memoryQueryArchived' | 'contextRewrite' | 'personaUpdate' | 'personaRemove' | 'personaClear' | 'taskCreate' | 'taskSetState' | 'taskDelete' | 'taskLinkInfo' | 'taskArchive' | 'taskQuery';
const TOOL_HANDLER_MAP: Record<string, ToolHandlerName> = {
'memory_recall': 'memoryRecal',
'memory_commit': 'memoryCommit',
'memory_purge': 'memoryPurge',
'memory_introspect': 'memoryIntrospect',
'memory_archive': 'memoryArchive',
'memory_cleanup': 'memoryCleanup',
'memory_query_archived': 'memoryQueryArchived',
'context_rewrite': 'contextRewrite',
'persona_update': 'personaUpdate',
'persona_remove': 'personaRemove',
'persona_clear': 'personaClear',
'task_create': 'taskCreate',
'task_set_state': 'taskSetState',
'task_delete': 'taskDelete',
'task_link_info': 'taskLinkInfo',
'task_archive': 'taskArchive',
'task_query': 'taskQuery',
};
// ========= AIAgentService =========
export class AIAgentService {
private memoryService: GraphMemoryService;
private currentSessionId: string;
private turnCounter: number = 0;
private appContext: common.Context;
constructor(memoryService: GraphMemoryService, appContext: common.Context, sessionId?: string) {
this.memoryService = memoryService;
this.appContext = appContext;
this.currentSessionId = sessionId || `session-hm-${Date.now()}`;
}
getSessionId(): string {
return this.currentSessionId;
}
/**
* 发送消息 — 完整的 Agent 流程
* 1. 查询人设
* 2. 查询工作记忆链
* 3. 注入上下文后请求 AI
* 4. 处理 tool_calls
* 5. 返回最终回复
*/
async sendMessage(userInput: string): Promise<AgentResponse> {
this.turnCounter++;
// === 步骤1+2: 获取上下文 ===
const personaResult = await this.memoryService.personaQuery();
const personaContext: string = personaResult.found ? this.formatPersona(personaResult.persona) : '';
const recallParams: MemoryRecallParams = {
queryIntent: 'TaskNode,工作记忆,任务链',
depth: 2
};
const taskResult = await this.memoryService.memoryRecall(recallParams);
// === 读取 API 配置 ===
const context = this.appContext;
const pref = await dataPreferences.getPreferences(context, 'trulymem_config');
const baseUrl: string = String(await pref.get('base_url', 'https://api.deepseek.com'));
const model: string = String(await pref.get('model', 'deepseek-chat'));
const apiKey: string = String(await pref.get('api_key', ''));
if (!apiKey) {
const noKeyResponse: AgentResponse = {
content: '⚠️ API Key 未配置,请先在设置页填写 API Key。',
toolCalls: []
};
return noKeyResponse;
}
// === 构建上下文丰富的消息 ===
const systemPrompt: string = buildSystemPrompt(personaContext);
const contextBlock: string = this.buildContextBlock(personaResult, taskResult);
const sysMsg: ApiRequestMessage = { role: 'system' as string, content: systemPrompt };
const userMsg: ApiRequestMessage = { role: 'user' as string, content: contextBlock + '\n\n---\n\n用户消息: ' + userInput };
const messages: ApiRequestMessage[] = [sysMsg, userMsg];
// === 步骤3: 请求 AI ===
const response: ApiResponse = await this.callApi(messages, baseUrl, model, apiKey);
const toolCalls: ToolCallResult[] = [];
// === 步骤4: 处理 tool_calls ===
if (response.choices && response.choices.length > 0) {
const choice: ApiChoice = response.choices[0];
const aiMessage: ApiChoiceMessage = choice.message;
// 处理函数调用
if (aiMessage.tool_calls && aiMessage.tool_calls.length > 0) {
for (const tc of aiMessage.tool_calls) {
const handlerName: ToolHandlerName | undefined = TOOL_HANDLER_MAP[tc.function.name];
if (handlerName) {
let args: Record<string, Object> = {};
try {
args = JSON.parse(tc.function.arguments);
}
catch (parseErr) {
const parseErrMsg = (parseErr as Error).message || JSON.stringify(parseErr);
defaultLogger.error('Failed to parse tool arguments: ' + parseErrMsg);
const badArgsResult: ToolCallResult = {
name: tc.function.name,
success: false,
message: '参数解析失败'
};
toolCalls.push(badArgsResult);
continue;
}
const result: ToolCallResult = await this.executeTool(handlerName, args);
toolCalls.push(result);
}
else {
const unknownToolResult: ToolCallResult = {
name: tc.function.name,
success: false,
message: '未知工具'
};
toolCalls.push(unknownToolResult);
}
}
// 有 tool_calls 时需要再次请求 AI带上工具执行结果
const followUpSystemMsg: ApiRequestMessage = { role: 'system', content: systemPrompt };
const followUpUserMsg: ApiRequestMessage = { role: 'user', content: contextBlock + '\n\n---\n\n用户消息: ' + userInput };
const followUpAssistantMsg: ApiRequestMessage = {
role: 'assistant',
content: aiMessage.content || '(已执行记忆操作)',
};
const toolResultsMessages: ApiRequestMessage[] = [
followUpSystemMsg,
followUpUserMsg,
followUpAssistantMsg,
];
for (const tc of aiMessage.tool_calls) {
const callResult: ToolCallResult | undefined = toolCalls.find(r => r.name === tc.function.name);
const toolResultMsg: string = callResult ? callResult.message : '完成';
const toolResultMessage: ApiRequestMessage = {
role: 'tool',
content: `工具 ${tc.function.name} 执行结果: ${toolResultMsg}`
};
toolResultsMessages.push(toolResultMessage);
}
const finalResponse: ApiResponse = await this.callApi(toolResultsMessages, baseUrl, model, apiKey);
if (finalResponse.choices && finalResponse.choices.length > 0) {
const content: string = finalResponse.choices[0].message.content || '';
const finalResult: AgentResponse = { content, toolCalls };
return finalResult;
}
}
// 普通回复(无 tool_calls
const content: string = aiMessage.content || '';
const noToolResponse: AgentResponse = { content, toolCalls };
return noToolResponse;
}
const noResponse: AgentResponse = {
content: 'AI 无响应',
toolCalls
};
return noResponse;
}
/**
* 请求 DeepSeek API
*/
private async callApi(messages: ApiRequestMessage[], baseUrl: string, model: string, apiKey: string): Promise<ApiResponse> {
const httpRequest = http.createHttp();
try {
const resp = await httpRequest.request(baseUrl + '/chat/completions', {
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + apiKey
},
extraData: {
model: model,
messages: messages,
tools: TOOLS_DEFINITION,
tool_choice: 'auto'
},
expectDataType: http.HttpDataType.OBJECT,
readTimeout: 60000
});
if (resp.responseCode === 200) {
return resp.result as ApiResponse;
}
const errorMsg: string = `API 请求失败: HTTP ${resp.responseCode}`;
throw new Error(errorMsg);
}
finally {
httpRequest.destroy();
}
}
/**
* 执行工具调用
*/
private async executeTool(name: ToolHandlerName, args: Record<string, Object>): Promise<ToolCallResult> {
try {
switch (name) {
case 'memoryRecal': {
const recallArgs: MemoryRecallParams = {
queryIntent: args.queryIntent as string,
seedEntities: args.seedEntities as string[],
depth: (args.depth as number) ?? 2,
timeRange: args.timeRange as TimeRangeParams,
sessionFilter: args.sessionFilter as string
};
const recallResult = await this.memoryService.memoryRecall(recallArgs);
const result: ToolCallResult = {
name: 'memory_recall',
success: true,
message: `找到 ${recallResult.entities.length} 个实体, ${recallResult.relations.length} 条关系`
};
return result;
}
case 'memoryCommit': {
const commitParams: MemoryCommitParams = {
triplets: args.triplets as TripletInput[],
entityTypes: args.entityTypes as Record<string, string>,
sessionId: (args.sessionId as string) || this.currentSessionId,
turnId: (args.turnId as number) || this.turnCounter
};
const commitResult = await this.memoryService.memoryCommit(commitParams);
const result: ToolCallResult = {
name: 'memory_commit',
success: true,
message: `已写入 ${commitResult.committedCount} 条记忆`
};
return result;
}
case 'memoryPurge': {
const purgeArgs: MemoryPurgeParams = {
criteria: args.criteria as PurgeCriteriaParams,
mode: args.mode as 'soft' | 'hard' | 'supersede',
newRelation: args.newRelation as NewRelationParams
};
const purgeResult = await this.memoryService.memoryPurge(purgeArgs);
const result: ToolCallResult = {
name: 'memory_purge',
success: true,
message: purgeResult.message
};
return result;
}
case 'memoryIntrospect': {
const introspectResult = await this.memoryService.memoryIntrospect(args.sessionId as string);
const result: ToolCallResult = {
name: 'memory_introspect',
success: true,
message: `实体: ${introspectResult.entityCount}, 关系: ${introspectResult.relationCount}, 热点: ${introspectResult.hotNodes.length}`
};
return result;
}
case 'memoryArchive': {
const archiveResult = await this.memoryService.archive(args.days as number);
const result: ToolCallResult = {
name: 'memory_archive',
success: true,
message: `已归档 ${archiveResult.archived} 条关系`
};
return result;
}
case 'memoryCleanup': {
const cleanupResult = await this.memoryService.cleanup((args.dryRun as boolean) !== false);
const result: ToolCallResult = {
name: 'memory_cleanup',
success: true,
message: `清理: ${cleanupResult.cleaned} 条关系, ${cleanupResult.deletedOrphans} 个孤儿节点` + (cleanupResult.dryRun ? ' (预览模式)' : '')
};
return result;
}
case 'memoryQueryArchived': {
const qaResult = await this.memoryService.queryArchived(args.days as number, args.keyword as string);
const result: ToolCallResult = {
name: 'memory_query_archived',
success: true,
message: `找到 ${qaResult.length} 条归档记录`
};
return result;
}
case 'contextRewrite': {
const summary = args.summary as string;
const result: ToolCallResult = {
name: 'context_rewrite',
success: summary.includes('[工具调用总结'),
message: summary.includes('[工具调用总结') ? '上下文已压缩' : '格式错误:必须包含[工具调用总结]标记'
};
return result;
}
case 'personaUpdate': {
const personaParams: PersonaUpdateParams = {
tone: args.tone as string,
style: args.style as string,
personality: args.personality as string,
catchphrase: args.catchphrase as string,
background: args.background as string
};
const puResult = await this.memoryService.personaUpdate(personaParams);
const result: ToolCallResult = {
name: 'persona_update',
success: puResult.success,
message: puResult.message
};
return result;
}
case 'personaRemove': {
const prResult = await this.memoryService.personaRemove(args.attribute as string);
const result: ToolCallResult = {
name: 'persona_remove',
success: prResult.success,
message: prResult.message
};
return result;
}
case 'personaClear': {
const pcResult = await this.memoryService.personaClear();
const result: ToolCallResult = {
name: 'persona_clear',
success: pcResult.success,
message: pcResult.message
};
return result;
}
case 'taskCreate': {
const createParams: TaskCreateParams = {
taskId: args.taskId as string,
description: args.description as string,
infoNodes: args.infoNodes as string[]
};
const tcResult = await this.memoryService.taskCreate(createParams);
const result: ToolCallResult = {
name: 'task_create',
success: tcResult.success,
message: tcResult.message
};
return result;
}
case 'taskSetState': {
const setStateParams: TaskSetStateParams = {
taskId: args.taskId as string,
state: args.state as ToolStateArg
};
const tsResult = await this.memoryService.taskSetState(setStateParams);
const result: ToolCallResult = {
name: 'task_set_state',
success: tsResult.success,
message: tsResult.message
};
return result;
}
case 'taskDelete': {
const deleteParams: TaskDeleteParams = {
taskId: args.taskId as string,
deleteInfoNodes: (args.deleteInfoNodes as boolean) !== false
};
const tdResult = await this.memoryService.taskDelete(deleteParams);
const result: ToolCallResult = {
name: 'task_delete',
success: tdResult.success,
message: tdResult.message
};
return result;
}
case 'taskLinkInfo': {
const linkInfoParams: TaskLinkInfoParams = {
taskId: args.taskId as string,
infoNodeNames: args.infoNodeNames as string[]
};
const tliResult = await this.memoryService.taskLinkInfo(linkInfoParams);
const result: ToolCallResult = {
name: 'task_link_info',
success: tliResult.success,
message: tliResult.message
};
return result;
}
case 'taskArchive': {
const archiveParams: TaskArchiveParams = {
taskId: args.taskId as string,
summary: args.summary as string
};
const taResult = await this.memoryService.taskArchive(archiveParams);
const result: ToolCallResult = {
name: 'task_archive',
success: taResult.success,
message: taResult.message
};
return result;
}
case 'taskQuery': {
const tqResult = await this.memoryService.taskQuery({
limit: args.limit as number,
stateFilter: args.stateFilter as string
});
const result: ToolCallResult = {
name: 'task_query',
success: true,
message: `找到 ${tqResult.tasks.length} 个任务`
};
return result;
}
default: {
const defaultResult: ToolCallResult = {
name: name as string,
success: false,
message: '未实现的工具'
};
return defaultResult;
}
}
}
catch (e) {
const errorMessage: string = (e as Error).message || '';
const errorResult: ToolCallResult = {
name: name as string,
success: false,
message: `执行失败: ${errorMessage}`
};
return errorResult;
}
}
/**
* 格式化人设数据为文本
*/
private formatPersona(persona: Record<string, string>): string {
const parts: string[] = [];
const keys: string[] = Object.keys(persona);
for (let i = 0; i < keys.length; i++) {
const key: string = keys[i];
const val: string = persona[key];
parts.push(`${key}: ${val}`);
}
return parts.length > 0 ? parts.join('') : '';
}
/**
* 构建上下文注入块
*/
private buildContextBlock(personaResult: PersonaQueryResult, taskResult: MemoryRecallResult): string {
const blocks: string[] = [];
if (personaResult.found) {
blocks.push(`【当前人设】\n${this.formatPersona(personaResult.persona)}`);
}
if (taskResult.entities.length > 0) {
const entitySample: EntityInfo[] = taskResult.entities.slice(0, 5);
const entitiesStr: string = JSON.stringify(entitySample);
blocks.push(`【工作记忆】\n${taskResult.message}\n${entitiesStr}`);
}
return blocks.length > 0 ? blocks.join('\n\n') : '【新对话】';
}
}

View File

@ -0,0 +1,935 @@
import type { GraphDatabase, RelationQueryResult, TimeRangeParams } from '../model/GraphDatabase';
import { defaultLogger } from "@normalized:N&&&@ohos/common/src/main/ets/util/Logger&1.0.0";
// ========= 接口定义 =========
export interface SnapshotData {
entities: EntityInfo[];
relations: RelationInfo[];
}
export interface GraphDataNode {
id: number;
label: string;
type: string;
mentions: number;
depth?: number;
}
export interface GraphDataEdge {
from: number;
to: number;
label: string;
weight: number;
depth?: number;
sessionId?: string;
turnId?: number;
}
export interface SnapshotEntity {
name: string;
type: string;
mention_count: number;
depth?: number;
}
export interface SnapshotRelation {
source: string;
target: string;
type: string;
confidence: number;
session_id?: string;
turn_id?: number;
depth?: number;
}
export interface GraphOutput {
nodes: GraphDataNode[];
edges: GraphDataEdge[];
}
export interface SnapshotOutput {
entities: SnapshotEntity[];
relations: SnapshotRelation[];
}
export interface EntityInfo {
name: string;
type: string;
mentionCount: number;
depth?: number;
}
export interface RelationInfo {
source: string;
target: string;
type: string;
confidence: number;
sessionId?: string;
turnId?: number;
depth?: number;
}
export interface TripletInput {
subject: string;
relation: string;
object: string;
confidence?: number;
}
export interface CleanupResult {
cleaned: number;
deletedRelations?: number;
deletedOrphans?: number;
dryRun?: boolean;
message: string;
}
export interface SearchResult {
name: string;
type: string;
mentions: number;
}
export interface TaskInfo {
taskId: string;
description: string;
state: string;
infoCount: number;
updatedAt: string;
}
export interface NodeData {
id: number;
label: string;
type: string;
mentions: number;
}
export interface EdgeData {
from: number;
to: number;
label: string;
weight: number;
}
export interface GraphData {
nodes: NodeData[];
edges: EdgeData[];
}
// 内部接口 — 用于替换内联对象类型声明
export interface PurgeCriteriaParams {
subjectContains?: string;
relationType?: string;
targetContains?: string;
sessionId?: string;
sourceType?: string;
targetType?: string;
sourceHasStatus?: string;
}
export interface NewRelationParams {
relation: string;
target: string;
}
interface TripletData {
subject: string;
relation: string;
object: string;
}
interface CriteriaData {
subject?: string;
target?: string;
relation?: string;
sessionId?: string;
}
export interface MemoryRecallResult {
entities: EntityInfo[];
relations: RelationInfo[];
message: string;
}
export interface MemoryRecallParams {
queryIntent: string;
seedEntities?: string[];
depth?: number;
timeRange?: TimeRangeParams;
sessionFilter?: string;
}
export interface MemoryCommitParams {
triplets: TripletInput[];
entityTypes?: Record<string, string>;
sessionId?: string;
turnId?: number;
}
interface MemoryCommitResult {
committedCount: number;
details: string[];
}
export interface MemoryPurgeParams {
criteria: PurgeCriteriaParams;
mode: 'soft' | 'hard' | 'supersede';
newRelation?: NewRelationParams;
}
interface MemoryPurgeResult {
deletedCount: number;
message: string;
}
interface HotNodeInfo {
name: string;
mentionCount: number;
type: string;
}
interface MemoryIntrospectResult {
entityCount: number;
relationCount: number;
hotNodes: HotNodeInfo[];
message: string;
}
interface ArchiveResult {
archived: number;
message: string;
}
interface PersonaResult {
success: boolean;
message: string;
}
export interface PersonaQueryResult {
persona: Record<string, string>;
found: boolean;
}
interface TaskCreateResult {
success: boolean;
taskId: string;
message: string;
}
interface TaskActionResult {
success: boolean;
message: string;
}
export interface TaskQueryResult {
tasks: TaskInfo[];
message: string;
}
export interface PersonaUpdateParams {
tone?: string;
style?: string;
personality?: string;
catchphrase?: string;
background?: string;
}
export interface TaskCreateParams {
taskId: string;
description: string;
infoNodes?: string[];
}
export interface TaskSetStateParams {
taskId: string;
state: '进行中' | '已完成' | '已暂停' | '已取消' | '已归档';
}
export interface TaskDeleteParams {
taskId: string;
deleteInfoNodes?: boolean;
}
export interface TaskLinkInfoParams {
taskId: string;
infoNodeNames: string[];
}
export interface TaskArchiveParams {
taskId: string;
summary?: string;
}
export interface TaskQueryParams {
limit?: number;
stateFilter?: string;
}
// 节点详情连接项接口
export interface ConnectionItem {
type: string;
target_name: string;
}
// 节点详情返回接口
export interface NodeDetailInfo {
name: string;
type: string;
mention_count: number;
connection_count: number;
connections: ConnectionItem[];
}
// 图数据统计接口
export interface GraphStats {
maxDegree: number;
avgDegree: number;
}
// 人设节点的固定名称
const PERSONA_NODE_NAME: string = 'trulymem_persona_identity';
const PERSONA_NODE_TYPE: string = 'PersonaNode';
// ========= GraphMemoryService =========
export class GraphMemoryService {
private db: GraphDatabase;
constructor(db: GraphDatabase) {
this.db = db;
}
// ========= 记忆操作 =========
/**
* 记忆召回 — 关键词搜索 + BFS 扩展
* 对应 tools.memory_recall
*/
async memoryRecall(params: MemoryRecallParams): Promise<MemoryRecallResult> {
const depth: number = params.depth ?? 2;
const result = await this.db.recall(params.queryIntent, params.seedEntities, depth, undefined, params.sessionFilter);
const entities: EntityInfo[] = result.entities.map(e => {
const entityItem: EntityInfo = {
name: e.name,
type: e.type,
mentionCount: e.mention_count,
depth: e.depth ?? 0
};
return entityItem;
});
const relations: RelationInfo[] = result.relations.map(r => {
const relationItem: RelationInfo = {
source: r.source,
target: r.target,
type: r.type,
confidence: r.confidence,
sessionId: r.session_id,
turnId: r.turn_id,
depth: r.depth ?? 0
};
return relationItem;
});
return { entities, relations, message: result.message };
}
/**
* 记忆写入 — 批量三元组
* 对应 tools.memory_commit
*/
async memoryCommit(params: MemoryCommitParams): Promise<MemoryCommitResult> {
const details: string[] = [];
for (const t of params.triplets) {
const subject = t.subject.trim();
const relation = t.relation.trim();
const object = t.object.trim();
if (!subject || !relation || !object) {
continue;
}
const triplets: TripletData[] = [{ subject, relation, object }];
await this.db.commit(triplets, params.entityTypes, params.sessionId, params.turnId);
details.push(`${subject} -[${relation}]-> ${object}`);
}
const result: MemoryCommitResult = {
committedCount: details.length,
details
};
return result;
}
/**
* 记忆删除
* 对应 tools.memory_purge
*/
async memoryPurge(params: MemoryPurgeParams): Promise<MemoryPurgeResult> {
// soft 模式:调用 db.purge 逻辑删除
if (params.mode === 'supersede' && params.newRelation) {
// supersede: 先软删除旧关系,再新建
const softSubject = params.criteria.subjectContains;
const softRelation = params.criteria.relationType;
const softTarget = params.criteria.targetContains;
await this.db.purge({
subject: softSubject,
relation: softRelation,
target: softTarget
}, 'soft');
// 新建替代关系
const newSubj = softSubject || '';
if (newSubj) {
await this.db.commit([{
subject: newSubj,
relation: params.newRelation.relation,
object: params.newRelation.target
}]);
}
const supersedeResult: MemoryPurgeResult = {
deletedCount: 1,
message: '已用 supersede 模式替代记忆'
};
return supersedeResult;
}
const subjContains = params.criteria.subjectContains;
const relType = params.criteria.relationType;
const tgtContains = params.criteria.targetContains;
const sessId = params.criteria.sessionId;
await this.db.purge({
subject: subjContains,
relation: relType,
target: tgtContains,
sessionId: sessId,
subjectContains: params.criteria.subjectContains,
targetContains: params.criteria.targetContains,
sourceType: params.criteria.sourceType,
targetType: params.criteria.targetType,
sourceHasStatus: params.criteria.sourceHasStatus
}, params.mode === 'hard' ? 'hard' : 'soft');
const purgeResult: MemoryPurgeResult = {
deletedCount: subjContains || tgtContains || relType || sessId ? 1 : 0,
message: `${params.mode === 'hard' ? '物理删除' : '软删除'}匹配的记忆`
};
return purgeResult;
}
/**
* 记忆状态查询
*/
async memoryIntrospect(sessionId?: string): Promise<MemoryIntrospectResult> {
const stats = await this.db.introspect();
const hotNodes: HotNodeInfo[] = [];
// 从所有节点中获取前10个高频节点
const searchAll = await this.db.search('');
const sorted = searchAll.sort((a, b) => b.mentions - a.mentions).slice(0, 10);
for (const n of sorted) {
const nodeInfo: HotNodeInfo = {
name: n.name,
mentionCount: n.mentions,
type: n.type
};
hotNodes.push(nodeInfo);
}
const result: MemoryIntrospectResult = {
entityCount: stats.entity_count,
relationCount: stats.relation_count,
hotNodes,
message: stats.message
};
return result;
}
/**
* 关键词搜索节点
*/
async search(keyword: string): Promise<SearchResult[]> {
return await this.db.search(keyword);
}
/**
* 归档旧记忆
*/
async archive(days: number): Promise<ArchiveResult> {
return await this.db.archive(days);
}
/**
* 清理已删除的记忆
*/
async cleanup(dryRun: boolean): Promise<CleanupResult> {
const result = await this.db.cleanup(dryRun);
const cleanupResult: CleanupResult = {
cleaned: result.cleaned,
deletedRelations: result.deleted_relations,
deletedOrphans: result.deleted_orphans,
dryRun: result.dry_run,
message: result.message || ''
};
return cleanupResult;
}
/**
* 记忆图谱 — 在指定时间范围内查询关系
* 对应 tools.memory_graph
*/
async memoryGraph(timeRange: TimeRangeParams, sessionFilter?: string): Promise<GraphOutput> {
const dbResult = await this.db.graph(timeRange, sessionFilter);
const nodeCount: number = dbResult.nodes.length;
const edgeCount: number = dbResult.edges.length;
const nodeList: GraphDataNode[] = [];
const edgeList: GraphDataEdge[] = [];
let idx: number = 0;
while (idx < nodeCount) {
const n = dbResult.nodes[idx];
const id1: number = n.id;
const label1: string = n.label;
const type1: string = n.type;
const mentions1: number = n.mentions;
const depth1: number | undefined = n.depth;
const graphNode: GraphDataNode = {
id: id1,
label: label1,
type: type1,
mentions: mentions1,
depth: depth1
};
nodeList.push(graphNode);
idx++;
}
idx = 0;
while (idx < edgeCount) {
const e = dbResult.edges[idx];
const from1: number = e.from;
const to1: number = e.to;
const label1: string = e.label;
const weight1: number = e.weight;
const depth1: number | undefined = e.depth;
const sessionId1: string | undefined = e.sessionId;
const turnId1: number | undefined = e.turnId;
const graphEdge: GraphDataEdge = {
from: from1,
to: to1,
label: label1,
weight: weight1,
depth: depth1,
sessionId: sessionId1,
turnId: turnId1
};
edgeList.push(graphEdge);
idx++;
}
const out: GraphOutput = {
nodes: nodeList,
edges: edgeList
};
return out;
}
/**
* 记忆快照 — 在指定时间范围内查询实体和关系
* 对应 tools.memory_snapshot
*/
async memorySnapshot(timeRange: TimeRangeParams, sessionFilter?: string): Promise<SnapshotOutput> {
const dbResult = await this.db.snapshot(timeRange, sessionFilter);
const entityCount: number = dbResult.entities.length;
const relationCount: number = dbResult.relations.length;
const entityList: SnapshotEntity[] = [];
const relationList: SnapshotRelation[] = [];
let idx: number = 0;
while (idx < entityCount) {
const e = dbResult.entities[idx];
const name1: string = e.name;
const type1: string = e.type;
const mentionCount1: number = e.mention_count;
const depth1: number | undefined = e.depth;
const entity: SnapshotEntity = {
name: name1,
type: type1,
mention_count: mentionCount1,
depth: depth1
};
entityList.push(entity);
idx++;
}
idx = 0;
while (idx < relationCount) {
const r = dbResult.relations[idx];
const source1: string = r.source;
const target1: string = r.target;
const type1: string = r.type;
const confidence1: number = r.confidence;
const sessionId1: string | undefined = r.session_id;
const turnId1: number | undefined = r.turn_id;
const depth1: number | undefined = r.depth;
const relation: SnapshotRelation = {
source: source1,
target: target1,
type: type1,
confidence: confidence1,
session_id: sessionId1,
turn_id: turnId1,
depth: depth1
};
relationList.push(relation);
idx++;
}
const out2: SnapshotOutput = {
entities: entityList,
relations: relationList
};
return out2;
}
/**
* 记忆清理 — 归档旧记忆并清理孤立节点
* 对应 tools.memory_cleanup
*/
async memoryCleanup(dryRun: boolean = false): Promise<CleanupResult> {
const result = await this.db.cleanup(dryRun);
const cleanupResult: CleanupResult = {
cleaned: result.cleaned,
deletedRelations: result.deleted_relations,
deletedOrphans: result.deleted_orphans,
dryRun: result.dry_run,
message: result.message || ''
};
return cleanupResult;
}
/**
* 记忆清理 — 删除指定条件的记忆
* 对应 tools.memory_purge
*/
/**
* 查询已归档的记忆
* 对应 tools.memory_query_archived
*/
async queryArchived(days?: number, keyword?: string): Promise<RelationQueryResult[]> {
try {
const result = await this.db.queryArchived(days, keyword);
return result;
}
catch (e) {
defaultLogger.error('queryArchived error: ' + JSON.stringify(e));
return [];
}
}
// ========= 人设管理 =========
/**
* 更新人设
* 对应 tools.persona_update
* 使用 PersonaNode + HAS_PERSONA 关系存储属性
*/
async personaUpdate(params: PersonaUpdateParams): Promise<PersonaResult> {
try {
// 1. 确保 PersonaNode 存在
const personaTriplets: TripletData[] = [];
const entityTypes: Record<string, string> = {};
entityTypes[PERSONA_NODE_NAME] = PERSONA_NODE_TYPE;
// 2. 逐个属性写入(作为关系),不使用 as any
const toneVal = params.tone;
const styleVal = params.style;
const personalityVal = params.personality;
const catchphraseVal = params.catchphrase;
const backgroundVal = params.background;
if (toneVal) {
personaTriplets.push({
subject: PERSONA_NODE_NAME,
relation: 'HAS_PERSONA_TONE',
object: toneVal
});
}
if (styleVal) {
personaTriplets.push({
subject: PERSONA_NODE_NAME,
relation: 'HAS_PERSONA_STYLE',
object: styleVal
});
}
if (personalityVal) {
personaTriplets.push({
subject: PERSONA_NODE_NAME,
relation: 'HAS_PERSONA_PERSONALITY',
object: personalityVal
});
}
if (catchphraseVal) {
personaTriplets.push({
subject: PERSONA_NODE_NAME,
relation: 'HAS_PERSONA_CATCHPHRASE',
object: catchphraseVal
});
}
if (backgroundVal) {
personaTriplets.push({
subject: PERSONA_NODE_NAME,
relation: 'HAS_PERSONA_BACKGROUND',
object: backgroundVal
});
}
if (personaTriplets.length > 0) {
await this.db.commit(personaTriplets, entityTypes);
}
const successResult: PersonaResult = {
success: true,
message: `已更新 ${personaTriplets.length} 个人设属性`
};
return successResult;
}
catch (e) {
const errorResult: PersonaResult = {
success: false,
message: `更新人设失败: ${(e as Error).message || ''}`
};
return errorResult;
}
}
/**
* 清除人设
* 对应 tools.persona_clear
*/
async personaClear(): Promise<PersonaResult> {
try {
await this.db.purge({ subject: PERSONA_NODE_NAME }, 'hard');
const result: PersonaResult = { success: true, message: '已清除所有人设信息' };
return result;
}
catch (e) {
const errorResult: PersonaResult = {
success: false,
message: `清除人设失败: ${(e as Error).message || ''}`
};
return errorResult;
}
}
/**
* 删除单条人设属性
* 对应 tools.persona_remove
*/
async personaRemove(attribute: string): Promise<PersonaResult> {
try {
// 将 attribute 转为关系名格式
const relationName = 'HAS_PERSONA_' + attribute.toUpperCase();
await this.db.purge({ subject: PERSONA_NODE_NAME, relation: relationName }, 'hard');
return {
success: true,
message: `已删除人设属性: ${attribute}`
};
}
catch (e) {
return {
success: false,
message: `删除人设属性失败: ${(e as Error).message || ''}`
};
}
}
/**
* 查询当前人设
*/
async personaQuery(): Promise<PersonaQueryResult> {
const result = await this.db.recall(PERSONA_NODE_NAME, [PERSONA_NODE_NAME], 2);
const persona: Record<string, string> = {};
for (const rel of result.relations) {
if (rel.source === PERSONA_NODE_NAME && rel.type.startsWith('HAS_PERSONA_')) {
const key = rel.type.replace('HAS_PERSONA_', '').toLowerCase();
persona[key] = rel.target;
}
}
const queryResult: PersonaQueryResult = {
persona,
found: Object.keys(persona).length > 0
};
return queryResult;
}
// ========= 任务管理 =========
/**
* 创建任务节点
* 对应 tools.task_create
*/
async taskCreate(params: TaskCreateParams): Promise<TaskCreateResult> {
try {
const entityTypes: Record<string, string> = {};
entityTypes[params.taskId] = 'TaskNode';
const triplets: TripletData[] = [
{ subject: params.taskId, relation: 'description', object: params.description },
{ subject: params.taskId, relation: 'has_state', object: '进行中' }
];
if (params.infoNodes && params.infoNodes.length > 0) {
for (const infoNode of params.infoNodes) {
entityTypes[infoNode] = 'InfoNode';
triplets.push({ subject: params.taskId, relation: 'CONTAINS_INFO', object: infoNode });
}
}
await this.db.commit(triplets, entityTypes);
const result: TaskCreateResult = {
success: true,
taskId: params.taskId,
message: `已创建任务: ${params.taskId}`
};
return result;
}
catch (e) {
const errorResult: TaskCreateResult = {
success: false,
taskId: params.taskId,
message: `创建任务失败: ${(e as Error).message || ''}`
};
return errorResult;
}
}
/**
* 设置任务状态
* 对应 tools.task_set_state
*/
async taskSetState(params: TaskSetStateParams): Promise<TaskActionResult> {
try {
// 先删旧的 has_state 关系,再新建
await this.db.purge({ subject: params.taskId, relation: 'has_state' }, 'soft');
await this.db.commit([{ subject: params.taskId, relation: 'has_state', object: params.state }]);
const result: TaskActionResult = {
success: true,
message: `任务 ${params.taskId} 状态已设为: ${params.state}`
};
return result;
}
catch (e) {
const errorResult: TaskActionResult = {
success: false,
message: `设置任务状态失败: ${(e as Error).message || ''}`
};
return errorResult;
}
}
/**
* 删除任务
* 对应 tools.task_delete
*/
async taskDelete(params: TaskDeleteParams): Promise<TaskActionResult> {
try {
// 删除所有关联关系
await this.db.purge({ subject: params.taskId }, 'hard');
if (params.deleteInfoNodes !== false) {
await this.db.purge({ target: params.taskId }, 'hard');
}
const result: TaskActionResult = { success: true, message: `已删除任务: ${params.taskId}` };
return result;
}
catch (e) {
const errorResult: TaskActionResult = {
success: false,
message: `删除任务失败: ${(e as Error).message || ''}`
};
return errorResult;
}
}
/**
* 关联信息节点到任务
* 对应 tools.task_link_info
*/
async taskLinkInfo(params: TaskLinkInfoParams): Promise<TaskActionResult> {
try {
const triplets: TripletData[] = [];
const entityTypes: Record<string, string> = {};
for (const nodeName of params.infoNodeNames) {
entityTypes[nodeName] = 'InfoNode';
triplets.push({ subject: params.taskId, relation: 'CONTAINS_INFO', object: nodeName });
}
await this.db.commit(triplets, entityTypes);
const result: TaskActionResult = { success: true, message: `已关联 ${triplets.length} 个信息节点` };
return result;
}
catch (e) {
const errorResult: TaskActionResult = {
success: false,
message: `关联信息节点失败: ${(e as Error).message || ''}`
};
return errorResult;
}
}
/**
* 归档任务
* 对应 tools.task_archive
*/
async taskArchive(params: TaskArchiveParams): Promise<TaskActionResult> {
try {
// 归档任务将状态设为已归档archived同时写入完成摘要
await this.taskSetState({ taskId: params.taskId, state: '已归档' });
if (params.summary) {
await this.db.commit([
{ subject: params.taskId, relation: 'archive_summary', object: params.summary }
]);
}
const result: TaskActionResult = { success: true, message: `已归档任务: ${params.taskId}` };
return result;
}
catch (e) {
const errorResult: TaskActionResult = {
success: false,
message: `归档任务失败: ${(e as Error).message || ''}`
};
return errorResult;
}
}
/**
* 查询任务列表
* 对应 tools.task_query
*/
async taskQuery(params?: TaskQueryParams): Promise<TaskQueryResult> {
const limit: number = params?.limit ?? 10;
const stateFilter: string | undefined = params?.stateFilter;
const queryResult: TaskQueryResult = {
tasks: await this.db.getRecentTasks(limit, stateFilter) as TaskInfo[],
message: `找到 ${limit} 个任务`
};
return queryResult;
}
// ========= 图数据 =========
/**
* 获取用于 WebView 的完整图数据
*/
async getGraphDataForView(): Promise<GraphData> {
// 用空关键词召回所有数据
const recallResult = await this.db.recall('', [], 3);
const nodes: NodeData[] = [];
const edges: EdgeData[] = [];
let nodeIdCounter = 1;
const nameToId: Record<string, number> = {};
for (const entity of recallResult.entities) {
const id = nodeIdCounter;
nodeIdCounter++;
nameToId[entity.name] = id;
const node: NodeData = {
id,
label: entity.name,
type: entity.type,
mentions: entity.mention_count
};
nodes.push(node);
}
for (const rel of recallResult.relations) {
const from = nameToId[rel.source];
const to = nameToId[rel.target];
if (from !== undefined && to !== undefined) {
const edge: EdgeData = {
from,
to,
label: rel.type,
weight: rel.confidence
};
edges.push(edge);
}
}
const graphData: GraphData = { nodes, edges };
return graphData;
}
/**
* 查询节点的完整信息 — 自身属性 + 所有相连关系
*/
async getNodeDetail(nodeName: string): Promise<NodeDetailInfo | null> {
try {
const recallResult = await this.db.recall(nodeName, [nodeName], 1);
if (recallResult.entities.length === 0) {
return null;
}
const entity = recallResult.entities[0];
const connections: ConnectionItem[] = [];
let connectionCount = 0;
for (const rel of recallResult.relations) {
if (rel.source === nodeName) {
const conn: ConnectionItem = {
type: rel.type,
target_name: rel.target
};
connections.push(conn);
connectionCount++;
}
else if (rel.target === nodeName) {
const conn: ConnectionItem = {
type: rel.type + ' (反向)',
target_name: rel.source
};
connections.push(conn);
connectionCount++;
}
}
const detail: NodeDetailInfo = {
name: entity.name,
type: entity.type,
mention_count: entity.mention_count,
connection_count: connectionCount,
connections
};
return detail;
}
catch (err) {
defaultLogger.error('getNodeDetail error: ' + JSON.stringify(err));
return null;
}
}
/**
* 返回带连接度数的图数据(每个节点增加 degree 字段)
*/
async getJoinedData(): Promise<GraphData> {
const graphData = await this.getGraphDataForView();
// 计算每个节点的连接度数
const degreeMap: Record<number, number> = {};
for (const edge of graphData.edges) {
degreeMap[edge.from] = (degreeMap[edge.from] || 0) + 1;
degreeMap[edge.to] = (degreeMap[edge.to] || 0) + 1;
}
// 手动为节点附加 degreeArkTS 不支持展开运算符)
const nodesWithDegree: NodeData[] = [];
for (let i = 0; i < graphData.nodes.length; i++) {
const orig = graphData.nodes[i];
const copy: NodeData = {
id: orig.id,
label: orig.label,
type: orig.type,
mentions: orig.mentions
};
nodesWithDegree.push(copy);
}
const graphResult: GraphData = {
nodes: nodesWithDegree,
edges: graphData.edges
};
return graphResult;
}
}

View File

@ -0,0 +1,43 @@
export enum WidthBreakpoint {
WIDTH_XS = "xs",
WIDTH_SM = "sm",
WIDTH_MD = "md",
WIDTH_LG = "lg",
WIDTH_XL = "xl"
}
export interface BreakpointTypes<T> {
xs?: T;
sm: T;
md: T;
lg: T;
xl?: T;
}
export class BreakpointType<T> {
private xs: T;
private sm: T;
private md: T;
private lg: T;
private xl: T;
public constructor(param: BreakpointTypes<T>) {
this.xs = param.xs ?? param.sm;
this.sm = param.sm;
this.md = param.md;
this.lg = param.lg;
this.xl = param.xl ?? param.lg;
}
public getValue(currentBreakpoint: WidthBreakpoint): T {
if (currentBreakpoint === WidthBreakpoint.WIDTH_XS) {
return this.xs;
}
if (currentBreakpoint === WidthBreakpoint.WIDTH_SM) {
return this.sm;
}
if (currentBreakpoint === WidthBreakpoint.WIDTH_MD) {
return this.md;
}
if (currentBreakpoint === WidthBreakpoint.WIDTH_XL) {
return this.xl;
}
return this.lg;
}
}

View File

@ -0,0 +1,24 @@
import hilog from "@ohos:hilog";
class Logger {
private domain: number;
private prefix: string;
private format: string = "%{public}s, %{public}s";
public constructor(prefix: string) {
this.prefix = prefix;
this.domain = 0xFF00;
}
public debug(...args: Object[]): void {
hilog.debug(this.domain, this.prefix, this.format, args);
}
public info(...args: Object[]): void {
hilog.info(this.domain, this.prefix, this.format, args);
}
public warn(...args: Object[]): void {
hilog.warn(this.domain, this.prefix, this.format, args);
}
public error(...args: Object[]): void {
hilog.error(this.domain, this.prefix, this.format, args);
}
}
export const defaultLogger = new Logger("[TrulyMEM]");
export default defaultLogger;

View File

@ -0,0 +1,42 @@
import { WidthBreakpoint } from "@normalized:N&&&@ohos/common/src/main/ets/util/BreakpointSystem&1.0.0";
export interface VMEvent {
}
export class BaseViewModel {
protected isAttached: boolean = false;
protected isDisposed: boolean = false;
protected currentBreakpoint: WidthBreakpoint = WidthBreakpoint.WIDTH_MD;
attach(): void {
if (this.isAttached) {
return;
}
this.isAttached = true;
this.onAttach();
}
detach(): void {
if (!this.isAttached) {
return;
}
this.isAttached = false;
this.onDetach();
}
dispose(): void {
if (this.isDisposed) {
return;
}
this.isDisposed = true;
this.detach();
this.onDispose();
}
protected onAttach(): void {
}
protected onDetach(): void {
}
protected onDispose(): void {
}
public get attached(): boolean {
return this.isAttached;
}
public get disposed(): boolean {
return this.isDisposed;
}
}

View File

@ -0,0 +1 @@
{"hspPkgNames":[],"compileEntries":["&@ohos/common/Index&1.0.0","&@ohos/common/src/main/ets/component/ImmersiveTabNavigation&1.0.0","&@ohos/common/src/main/ets/constant/TrulyMEMConstants&1.0.0","&@ohos/common/src/main/ets/model/GraphDatabase&1.0.0","&@ohos/common/src/main/ets/routermanager/PageContext&1.0.0","&@ohos/common/src/main/ets/service/AIAgentService&1.0.0","&@ohos/common/src/main/ets/service/GraphMemoryService&1.0.0","&@ohos/common/src/main/ets/util/BreakpointSystem&1.0.0","&@ohos/common/src/main/ets/util/Logger&1.0.0","&@ohos/common/src/main/ets/viewmodel/BaseViewModel&1.0.0","&@ohos/graph/Index&1.0.0","&@ohos/graph/src/main/ets/components/GraphComponents&1.0.0","&@ohos/graph/src/main/ets/pages/GraphPage&1.0.0","&@ohos/chat/Index&1.0.0","&@ohos/chat/src/main/ets/components/ChatComponents&1.0.0","&@ohos/chat/src/main/ets/pages/ChatPage&1.0.0","&@ohos/settings/Index&1.0.0","&@ohos/settings/src/main/ets/components/SettingsComponents&1.0.0","&@ohos/settings/src/main/ets/pages/SettingsPage&1.0.0","&@ohos/phone/src/main/ets/pages/MainPage&","&phone/build/generated/r/ResourceTable&","&@ohos/phone/src/main/ets/entryability/EntryAbility&","&@ohos/phone/src/main/ets/pages/SplashPage&","&@ohos/phone/src/main/ets/pages/Index&"],"updateVersionInfo":{}}

View File

@ -0,0 +1 @@
{"resolveConflictMode":true,"depName2RootPath":{"@ohos/common":"/home/program/TrulyMEM-TrueHumanMEM/common","@ohos/graph":"/home/program/TrulyMEM-TrueHumanMEM/features/graph","@ohos/chat":"/home/program/TrulyMEM-TrueHumanMEM/features/chat","@ohos/settings":"/home/program/TrulyMEM-TrueHumanMEM/features/settings","@ohos/hypium":"/home/program/TrulyMEM-TrueHumanMEM/oh_modules/.ohpm/@ohos+hypium@1.0.24/oh_modules/@ohos/hypium","@ohos/hamock":"/home/program/TrulyMEM-TrueHumanMEM/oh_modules/.ohpm/@ohos+hamock@1.0.0/oh_modules/@ohos/hamock"},"depName2DepInfo":{"@ohos/common":{"dependencyType":"har","isByteCodeHar":false,"pkgRootPath":"/home/program/TrulyMEM-TrueHumanMEM/common","pkgName":"@ohos/common","pkgVersion":"1.0.0"},"@ohos/graph":{"dependencyType":"har","isByteCodeHar":false,"pkgRootPath":"/home/program/TrulyMEM-TrueHumanMEM/features/graph","pkgName":"@ohos/graph","pkgVersion":"1.0.0"},"@ohos/chat":{"dependencyType":"har","isByteCodeHar":false,"pkgRootPath":"/home/program/TrulyMEM-TrueHumanMEM/features/chat","pkgName":"@ohos/chat","pkgVersion":"1.0.0"},"@ohos/settings":{"dependencyType":"har","isByteCodeHar":false,"pkgRootPath":"/home/program/TrulyMEM-TrueHumanMEM/features/settings","pkgName":"@ohos/settings","pkgVersion":"1.0.0"},"@ohos/hypium":{"dependencyType":"har","isByteCodeHar":false,"pkgRootPath":"/home/program/TrulyMEM-TrueHumanMEM/oh_modules/.ohpm/@ohos+hypium@1.0.24/oh_modules/@ohos/hypium","pkgName":"@ohos/hypium","pkgVersion":"1.0.24"},"@ohos/hamock":{"dependencyType":"har","isByteCodeHar":false,"pkgRootPath":"/home/program/TrulyMEM-TrueHumanMEM/oh_modules/.ohpm/@ohos+hamock@1.0.0/oh_modules/@ohos/hamock","pkgName":"@ohos/hamock","pkgVersion":"1.0.0"}}}

View File

@ -0,0 +1,24 @@
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/Index.ts;&@ohos/common/Index&1.0.0;esm;@ohos/phone|@ohos/common|1.0.0|Index.ts;@ohos/common;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/constant/TrulyMEMConstants.ts;&@ohos/common/src/main/ets/constant/TrulyMEMConstants&1.0.0;esm;@ohos/phone|@ohos/common|1.0.0|src/main/ets/constant/TrulyMEMConstants.ts;@ohos/common;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/component/ImmersiveTabNavigation.ts;&@ohos/common/src/main/ets/component/ImmersiveTabNavigation&1.0.0;esm;@ohos/phone|@ohos/common|1.0.0|src/main/ets/component/ImmersiveTabNavigation.ts;@ohos/common;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/routermanager/PageContext.ts;&@ohos/common/src/main/ets/routermanager/PageContext&1.0.0;esm;@ohos/phone|@ohos/common|1.0.0|src/main/ets/routermanager/PageContext.ts;@ohos/common;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/service/AIAgentService.ts;&@ohos/common/src/main/ets/service/AIAgentService&1.0.0;esm;@ohos/phone|@ohos/common|1.0.0|src/main/ets/service/AIAgentService.ts;@ohos/common;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/service/GraphMemoryService.ts;&@ohos/common/src/main/ets/service/GraphMemoryService&1.0.0;esm;@ohos/phone|@ohos/common|1.0.0|src/main/ets/service/GraphMemoryService.ts;@ohos/common;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/util/BreakpointSystem.ts;&@ohos/common/src/main/ets/util/BreakpointSystem&1.0.0;esm;@ohos/phone|@ohos/common|1.0.0|src/main/ets/util/BreakpointSystem.ts;@ohos/common;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/util/Logger.ts;&@ohos/common/src/main/ets/util/Logger&1.0.0;esm;@ohos/phone|@ohos/common|1.0.0|src/main/ets/util/Logger.ts;@ohos/common;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/viewmodel/BaseViewModel.ts;&@ohos/common/src/main/ets/viewmodel/BaseViewModel&1.0.0;esm;@ohos/phone|@ohos/common|1.0.0|src/main/ets/viewmodel/BaseViewModel.ts;@ohos/common;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/model/GraphDatabase.ts;&@ohos/common/src/main/ets/model/GraphDatabase&1.0.0;esm;@ohos/phone|@ohos/common|1.0.0|src/main/ets/model/GraphDatabase.ts;@ohos/common;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/graph/Index.ts;&@ohos/graph/Index&1.0.0;esm;@ohos/phone|@ohos/graph|1.0.0|Index.ts;@ohos/graph;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/graph/src/main/ets/pages/GraphPage.ts;&@ohos/graph/src/main/ets/pages/GraphPage&1.0.0;esm;@ohos/phone|@ohos/graph|1.0.0|src/main/ets/pages/GraphPage.ts;@ohos/graph;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/graph/src/main/ets/components/GraphComponents.ts;&@ohos/graph/src/main/ets/components/GraphComponents&1.0.0;esm;@ohos/phone|@ohos/graph|1.0.0|src/main/ets/components/GraphComponents.ts;@ohos/graph;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/chat/Index.ts;&@ohos/chat/Index&1.0.0;esm;@ohos/phone|@ohos/chat|1.0.0|Index.ts;@ohos/chat;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/chat/src/main/ets/pages/ChatPage.ts;&@ohos/chat/src/main/ets/pages/ChatPage&1.0.0;esm;@ohos/phone|@ohos/chat|1.0.0|src/main/ets/pages/ChatPage.ts;@ohos/chat;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/chat/src/main/ets/components/ChatComponents.ts;&@ohos/chat/src/main/ets/components/ChatComponents&1.0.0;esm;@ohos/phone|@ohos/chat|1.0.0|src/main/ets/components/ChatComponents.ts;@ohos/chat;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/settings/Index.ts;&@ohos/settings/Index&1.0.0;esm;@ohos/phone|@ohos/settings|1.0.0|Index.ts;@ohos/settings;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/settings/src/main/ets/components/SettingsComponents.ts;&@ohos/settings/src/main/ets/components/SettingsComponents&1.0.0;esm;@ohos/phone|@ohos/settings|1.0.0|src/main/ets/components/SettingsComponents.ts;@ohos/settings;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/settings/src/main/ets/pages/SettingsPage.ts;&@ohos/settings/src/main/ets/pages/SettingsPage&1.0.0;esm;@ohos/phone|@ohos/settings|1.0.0|src/main/ets/pages/SettingsPage.ts;@ohos/settings;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/phone/src/main/ets/pages/MainPage.ts;&@ohos/phone/src/main/ets/pages/MainPage&;esm;@ohos/phone|@ohos/phone|1.0.0|src/main/ets/pages/MainPage.ts;@ohos/phone;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/phone/build/default/generated/r/default/ResourceTable.js;&phone/build/generated/r/ResourceTable&;esm;@ohos/phone|@ohos/phone|1.0.0|build/default/generated/r/default/ResourceTable.js;@ohos/phone;false;ts
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/phone/src/main/ets/entryability/EntryAbility.ts;&@ohos/phone/src/main/ets/entryability/EntryAbility&;esm;@ohos/phone|@ohos/phone|1.0.0|src/main/ets/entryability/EntryAbility.ts;@ohos/phone;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/phone/src/main/ets/pages/SplashPage.ts;&@ohos/phone/src/main/ets/pages/SplashPage&;esm;@ohos/phone|@ohos/phone|1.0.0|src/main/ets/pages/SplashPage.ts;@ohos/phone;false;ets
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/phone/src/main/ets/pages/Index.ts;&@ohos/phone/src/main/ets/pages/Index&;esm;@ohos/phone|@ohos/phone|1.0.0|src/main/ets/pages/Index.ts;@ohos/phone;false;ets

View File

@ -0,0 +1 @@
export { GraphPage } from "@normalized:N&&&@ohos/graph/src/main/ets/pages/GraphPage&1.0.0";

View File

@ -0,0 +1,390 @@
if (!("finalizeConstruction" in ViewPU.prototype)) {
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface GraphWebView_Params {
controller?: web_webview.WebviewController;
bridge?: NativeBridge;
onPageEnd?: () => void;
}
interface NodeDetailPanel_Params {
detail?: NodeDetailInfo;
onClose?: () => void;
}
interface GraphNodeSearchBar_Params {
searchText?: string;
onSearchInput?: (value: string) => void;
}
import web_webview from "@ohos:web.webview";
import { Logger } from "@normalized:N&&&@ohos/common/Index&1.0.0";
import type { GraphDatabase, RecallEntity, ConnectionItem, NodeDetailInfo } from "@normalized:N&&&@ohos/common/Index&1.0.0";
export class GraphNodeSearchBar extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.__searchText = new SynchedPropertySimpleTwoWayPU(params.searchText, this, "searchText");
this.onSearchInput = undefined;
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: GraphNodeSearchBar_Params) {
if (params.onSearchInput !== undefined) {
this.onSearchInput = params.onSearchInput;
}
}
updateStateVars(params: GraphNodeSearchBar_Params) {
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__searchText.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__searchText.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private __searchText: SynchedPropertySimpleTwoWayPU<string>;
get searchText() {
return this.__searchText.get();
}
set searchText(newValue: string) {
this.__searchText.set(newValue);
}
private onSearchInput?: (value: string) => void;
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.width('100%');
Column.position({ x: 0, y: 0 });
Column.zIndex(10);
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
TextInput.create({ placeholder: '搜索节点...', text: this.searchText });
TextInput.width('80%');
TextInput.height(40);
TextInput.backgroundColor('rgba(10, 10, 26, 0.8)');
TextInput.fontColor('#ffffff');
TextInput.placeholderColor('#666688');
TextInput.borderRadius(8);
TextInput.border({ width: 1, color: 'rgba(100, 100, 255, 0.3)' });
TextInput.margin({ top: 20 });
TextInput.onChange((value: string) => {
this.onSearchInput?.(value);
});
}, TextInput);
Column.pop();
}
rerender() {
this.updateDirtyElements();
}
}
export class NodeDetailPanel extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.__detail = new SynchedPropertyObjectOneWayPU(params.detail, this, "detail");
this.onClose = undefined;
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: NodeDetailPanel_Params) {
if (params.onClose !== undefined) {
this.onClose = params.onClose;
}
}
updateStateVars(params: NodeDetailPanel_Params) {
this.__detail.reset(params.detail);
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__detail.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__detail.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private __detail: SynchedPropertySimpleOneWayPU<NodeDetailInfo>;
get detail() {
return this.__detail.get();
}
set detail(newValue: NodeDetailInfo) {
this.__detail.set(newValue);
}
private onClose?: () => void;
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.width('100%');
Column.height('100%');
Column.backgroundColor('rgba(0, 0, 0, 0.5)');
Column.justifyContent(FlexAlign.Center);
Column.alignItems(HorizontalAlign.Center);
Column.zIndex(20);
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.padding(20);
Column.backgroundColor('rgba(10, 10, 26, 0.95)');
Column.borderRadius(12);
Column.border({ width: 1, color: 'rgba(100, 100, 255, 0.3)' });
Column.width(300);
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(this.detail.name);
Text.fontSize(18);
Text.fontColor('#44ff88');
Text.fontWeight(FontWeight.Bold);
Text.margin({ bottom: 10 });
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create('类型: ' + this.detail.type);
Text.fontSize(14);
Text.fontColor('#aaaacc');
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create('提及次数: ' + this.detail.mention_count);
Text.fontSize(14);
Text.fontColor('#aaaacc');
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create('连接数: ' + this.detail.connection_count);
Text.fontSize(14);
Text.fontColor('#aaaacc');
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
If.create();
if (this.detail.connections && this.detail.connections.length > 0) {
this.ifElseBranchUpdateFunction(0, () => {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create('连接关系:');
Text.fontSize(14);
Text.fontColor('#8888aa');
Text.margin({ top: 10, bottom: 5 });
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
List.create();
List.height(100);
}, List);
this.observeComponentCreation2((elmtId, isInitialRender) => {
ForEach.create();
const forEachItemGenFunction = _item => {
const conn = _item;
{
const itemCreation = (elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
ListItem.create(deepRenderFunction, true);
if (!isInitialRender) {
ListItem.pop();
}
ViewStackProcessor.StopGetAccessRecording();
};
const itemCreation2 = (elmtId, isInitialRender) => {
ListItem.create(deepRenderFunction, true);
};
const deepRenderFunction = (elmtId, isInitialRender) => {
itemCreation(elmtId, isInitialRender);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(conn.type + ': ' + conn.target_name);
Text.fontSize(12);
Text.fontColor('#aaaacc');
}, Text);
Text.pop();
ListItem.pop();
};
this.observeComponentCreation2(itemCreation2, ListItem);
ListItem.pop();
}
};
this.forEachUpdateFunction(elmtId, this.detail.connections, forEachItemGenFunction);
}, ForEach);
ForEach.pop();
List.pop();
});
}
else {
this.ifElseBranchUpdateFunction(1, () => {
});
}
}, If);
If.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Button.createWithLabel('关闭');
Button.width(80);
Button.height(30);
Button.margin({ top: 15 });
Button.backgroundColor('rgba(100, 100, 255, 0.3)');
Button.fontColor('#ffffff');
Button.onClick(() => {
this.onClose?.();
});
}, Button);
Button.pop();
Column.pop();
Column.pop();
}
rerender() {
this.updateDirtyElements();
}
}
export class GraphWebView extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.controller = new web_webview.WebviewController();
this.bridge = undefined;
this.onPageEnd = undefined;
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: GraphWebView_Params) {
if (params.controller !== undefined) {
this.controller = params.controller;
}
if (params.bridge !== undefined) {
this.bridge = params.bridge;
}
if (params.onPageEnd !== undefined) {
this.onPageEnd = params.onPageEnd;
}
}
updateStateVars(params: GraphWebView_Params) {
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
}
aboutToBeDeleted() {
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private controller: web_webview.WebviewController;
private bridge?: NativeBridge;
private onPageEnd?: () => void;
getController(): web_webview.WebviewController {
return this.controller;
}
setBridge(bridge: NativeBridge): void {
this.bridge = bridge;
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Web.create({ src: { "id": 0, "type": 30000, params: ['graph.html'], "bundleName": "com.trulymem.app", "moduleName": "phone" }, controller: this.controller });
Web.javaScriptAccess(true);
Web.width('100%');
Web.height('100%');
Web.zoomAccess(true);
Web.onPageEnd(() => {
this.onPageEnd?.();
});
Web.javaScriptProxy({
object: this.bridge,
name: 'nativeBridge',
methodList: ['onNodeClick', 'onSearch'],
asyncMethodList: ['requestGraphData'],
controller: this.controller
});
}, Web);
}
rerender() {
this.updateDirtyElements();
}
}
/**
* NativeBridge — WebView 原生桥接类(移动自 GraphPage
* 负责 ArkTS ↔ WebView JavaScript 双向通信
*/
export class NativeBridge {
private controller: web_webview.WebviewController;
private onRequestGraphData: () => void;
private onNodeClickCallback: (nodeId: number, nodeName: string) => void;
private onSearchCallback: (query: string) => void;
constructor(controller: web_webview.WebviewController, onRequestGraphData: () => void, onNodeClickCallback: (nodeId: number, nodeName: string) => void, onSearchCallback: (query: string) => void) {
this.controller = controller;
this.onRequestGraphData = onRequestGraphData;
this.onNodeClickCallback = onNodeClickCallback;
this.onSearchCallback = onSearchCallback;
}
onNodeClick(nodeId: number, nodeName: string): void {
Logger.info('Node clicked: id=' + nodeId + ', name=' + nodeName);
if (this.onNodeClickCallback) {
this.onNodeClickCallback(nodeId, nodeName);
}
}
onSearch(query: string): void {
Logger.info('Search from WebView: ' + query);
if (this.onSearchCallback) {
this.onSearchCallback(query);
}
}
requestGraphData(): void {
Logger.info('requestGraphData called from WebView');
if (this.onRequestGraphData) {
this.onRequestGraphData();
}
}
}
/**
* GraphDataService — 图数据查询服务
* 封装从 GraphDatabase 读取节点和边的逻辑
*/
export class GraphDataService {
private db: GraphDatabase;
constructor(db: GraphDatabase) {
this.db = db;
}
async getAllNodes(): Promise<GraphNodeItem[]> {
const result = await this.db.search('');
return result.map((r, idx): GraphNodeItem => {
return {
id: idx + 1,
label: r.name,
type: r.type,
mentions: r.mentions
};
});
}
async getAllEdges(): Promise<GraphEdgeItem[]> {
const recallResult = await this.db.recall('', [], 3);
const nameToId: Record<string, number> = {};
recallResult.entities.forEach((e: RecallEntity, idx: number): void => {
nameToId[e.name as string] = idx + 1;
});
const edgeItems: GraphEdgeItem[] = [];
for (let i = 0; i < recallResult.relations.length; i++) {
const r = recallResult.relations[i];
const sourceId = nameToId[r.source];
const targetId = nameToId[r.target];
if (sourceId !== undefined && targetId !== undefined) {
edgeItems.push({
id: i + 1,
source: sourceId,
target: targetId,
label: r.type,
relation: r.type
});
}
}
return edgeItems;
}
}
// ========= 内部类型定义 =========
interface GraphNodeItem {
id: number;
label: string;
type: string;
mentions: number;
}
interface GraphEdgeItem {
id: number;
source: number;
target: number;
label: string;
relation: string;
}

View File

@ -0,0 +1,341 @@
if (!("finalizeConstruction" in ViewPU.prototype)) {
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface GraphPage_Params {
controller?: web_webview.WebviewController;
db?: GraphDatabase;
nodeCount?: number;
edgeCount?: number;
selectedNodeDetail?: NodeDetailInfo | null;
showNodeDetail?: boolean;
searchText?: string;
graphService?: GraphMemoryService;
bridge?: NativeBridge;
}
import web_webview from "@ohos:web.webview";
import { Logger, GraphMemoryService } from "@normalized:N&&&@ohos/common/Index&1.0.0";
import type { GraphDatabase, NodeDetailInfo, RecallEntity } from "@normalized:N&&&@ohos/common/Index&1.0.0";
import { GraphNodeSearchBar, NodeDetailPanel, NativeBridge } from "@normalized:N&&&@ohos/graph/src/main/ets/components/GraphComponents&1.0.0";
// ========= GraphPage 组件 =========
interface GraphNodeItem {
id: number;
label: string;
type: string;
mentions: number;
}
interface GraphEdgeItem {
id: number;
source: number;
target: number;
label: string;
relation: string;
}
export class GraphPage extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.controller = new web_webview.WebviewController();
this.__db = new SynchedPropertyObjectOneWayPU(params.db, this, "db");
this.__nodeCount = new ObservedPropertySimplePU(0, this, "nodeCount");
this.__edgeCount = new ObservedPropertySimplePU(0, this, "edgeCount");
this.__selectedNodeDetail = new ObservedPropertyObjectPU(null, this, "selectedNodeDetail");
this.__showNodeDetail = new ObservedPropertySimplePU(false, this, "showNodeDetail");
this.__searchText = new ObservedPropertySimplePU('', this, "searchText");
this.graphService = undefined;
this.bridge = undefined;
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: GraphPage_Params) {
if (params.controller !== undefined) {
this.controller = params.controller;
}
if (params.nodeCount !== undefined) {
this.nodeCount = params.nodeCount;
}
if (params.edgeCount !== undefined) {
this.edgeCount = params.edgeCount;
}
if (params.selectedNodeDetail !== undefined) {
this.selectedNodeDetail = params.selectedNodeDetail;
}
if (params.showNodeDetail !== undefined) {
this.showNodeDetail = params.showNodeDetail;
}
if (params.searchText !== undefined) {
this.searchText = params.searchText;
}
if (params.graphService !== undefined) {
this.graphService = params.graphService;
}
if (params.bridge !== undefined) {
this.bridge = params.bridge;
}
}
updateStateVars(params: GraphPage_Params) {
this.__db.reset(params.db);
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__db.purgeDependencyOnElmtId(rmElmtId);
this.__nodeCount.purgeDependencyOnElmtId(rmElmtId);
this.__edgeCount.purgeDependencyOnElmtId(rmElmtId);
this.__selectedNodeDetail.purgeDependencyOnElmtId(rmElmtId);
this.__showNodeDetail.purgeDependencyOnElmtId(rmElmtId);
this.__searchText.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__db.aboutToBeDeleted();
this.__nodeCount.aboutToBeDeleted();
this.__edgeCount.aboutToBeDeleted();
this.__selectedNodeDetail.aboutToBeDeleted();
this.__showNodeDetail.aboutToBeDeleted();
this.__searchText.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private controller: web_webview.WebviewController;
private __db: SynchedPropertySimpleOneWayPU<GraphDatabase>;
get db() {
return this.__db.get();
}
set db(newValue: GraphDatabase) {
this.__db.set(newValue);
}
private __nodeCount: ObservedPropertySimplePU<number>;
get nodeCount() {
return this.__nodeCount.get();
}
set nodeCount(newValue: number) {
this.__nodeCount.set(newValue);
}
private __edgeCount: ObservedPropertySimplePU<number>;
get edgeCount() {
return this.__edgeCount.get();
}
set edgeCount(newValue: number) {
this.__edgeCount.set(newValue);
}
private __selectedNodeDetail: ObservedPropertyObjectPU<NodeDetailInfo | null>;
get selectedNodeDetail() {
return this.__selectedNodeDetail.get();
}
set selectedNodeDetail(newValue: NodeDetailInfo | null) {
this.__selectedNodeDetail.set(newValue);
}
private __showNodeDetail: ObservedPropertySimplePU<boolean>;
get showNodeDetail() {
return this.__showNodeDetail.get();
}
set showNodeDetail(newValue: boolean) {
this.__showNodeDetail.set(newValue);
}
private __searchText: ObservedPropertySimplePU<string>;
get searchText() {
return this.__searchText.get();
}
set searchText(newValue: string) {
this.__searchText.set(newValue);
}
private graphService: GraphMemoryService;
private bridge: NativeBridge;
aboutToAppear() {
this.graphService = new GraphMemoryService(this.db);
this.bridge = new NativeBridge(this.controller, (): void => { this.pushGraphDataToWebView(); }, (nodeId: number, nodeName: string): void => { this.handleNodeClick(nodeId, nodeName); }, (query: string): void => { this.handleSearchFromWeb(query); });
}
/**
* 外部触发刷新图数据(聊天写入新记忆后调用)
*/
public async refreshGraphData(): Promise<void> {
await this.pushGraphDataToWebView();
}
/**
* 处理节点点击 - 查询详细信息并显示浮层
*/
private async handleNodeClick(nodeId: number, nodeName: string): Promise<void> {
try {
if (!this.graphService)
return;
const detail: NodeDetailInfo | null = await this.graphService.getNodeDetail(nodeName);
if (detail) {
this.selectedNodeDetail = detail;
this.showNodeDetail = true;
}
}
catch (err) {
Logger.error('handleNodeClick error: ' + JSON.stringify(err));
}
}
/**
* 处理来自 WebView 的搜索请求
*/
private handleSearchFromWeb(query: string): void {
this.searchText = query;
}
/**
* 处理搜索输入 - 通知 WebView 过滤
*/
private onSearchInput(value: string): void {
this.searchText = value;
const escapedValue = value.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/"/g, '\\"');
const jsCode = `window.dispatchEvent(new MessageEvent('message', { data: { type: 'search_nodes', query: '${escapedValue}' } }));`;
this.controller.runJavaScript(jsCode);
}
/**
* 关闭节点详情浮层
*/
private closeNodeDetail(): void {
this.showNodeDetail = false;
this.selectedNodeDetail = null;
}
/**
* 从数据库读取全量图数据,推送给 WebView
*/
private async getAllNodesData(): Promise<GraphNodeItem[]> {
const result = await this.db.search('');
return result.map((r, idx): GraphNodeItem => {
return {
id: idx + 1,
label: r.name,
type: r.type,
mentions: r.mentions
};
});
}
private async getAllEdgesData(): Promise<GraphEdgeItem[]> {
const recallResult = await this.db.recall('', [], 3);
const nameToId: Record<string, number> = {};
recallResult.entities.forEach((e: RecallEntity, idx: number): void => {
nameToId[e.name as string] = idx + 1;
});
const edgeItems: GraphEdgeItem[] = [];
for (let i = 0; i < recallResult.relations.length; i++) {
const r = recallResult.relations[i];
const sourceId: number | undefined = nameToId[r.source];
const targetId: number | undefined = nameToId[r.target];
if (sourceId !== undefined && targetId !== undefined) {
edgeItems.push({
id: i + 1,
source: sourceId,
target: targetId,
label: r.type,
relation: r.type
});
}
}
return edgeItems;
}
/**
* 从数据库读取全量图数据,推送给 WebView
*/
private async pushGraphDataToWebView(): Promise<void> {
try {
const allNodes: GraphNodeItem[] = await this.getAllNodesData();
const allEdges: GraphEdgeItem[] = await this.getAllEdgesData();
if (this.controller) {
const jsCode: string = `window.loadGraphData(${JSON.stringify({ nodes: allNodes, edges: allEdges })});`;
this.controller.runJavaScript(jsCode);
}
this.nodeCount = allNodes.length;
this.edgeCount = allEdges.length;
}
catch (err) {
Logger.error('pushGraphDataToWebView error: ' + JSON.stringify(err));
}
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Stack.create();
Stack.width('100%');
Stack.height('100%');
}, Stack);
this.observeComponentCreation2((elmtId, isInitialRender) => {
// WebView 显示 3D 星图
Web.create({ src: { "id": 0, "type": 30000, params: ['graph.html'], "bundleName": "com.trulymem.app", "moduleName": "phone" }, controller: this.controller });
// WebView 显示 3D 星图
Web.javaScriptAccess(true);
// WebView 显示 3D 星图
Web.width('100%');
// WebView 显示 3D 星图
Web.height('100%');
// WebView 显示 3D 星图
Web.zoomAccess(true);
// WebView 显示 3D 星图
Web.onPageEnd(() => {
this.pushGraphDataToWebView();
});
// WebView 显示 3D 星图
Web.javaScriptProxy({
object: this.bridge,
name: 'nativeBridge',
methodList: ['onNodeClick', 'onSearch'],
asyncMethodList: ['requestGraphData'],
controller: this.controller
});
}, Web);
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new
// 搜索框
GraphNodeSearchBar(this, {
searchText: this.__searchText,
onSearchInput: (value: string): void => { this.onSearchInput(value); }
}, undefined, elmtId, () => { }, { page: "features/graph/src/main/ets/pages/GraphPage.ets", line: 189, col: 13 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
searchText: this.searchText,
onSearchInput: (value: string): void => { this.onSearchInput(value); }
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {});
}
}, { name: "GraphNodeSearchBar" });
}
this.observeComponentCreation2((elmtId, isInitialRender) => {
If.create();
// 节点详情浮层
if (this.showNodeDetail && this.selectedNodeDetail !== null) {
this.ifElseBranchUpdateFunction(0, () => {
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new NodeDetailPanel(this, {
detail: this.selectedNodeDetail,
onClose: (): void => { this.closeNodeDetail(); }
}, undefined, elmtId, () => { }, { page: "features/graph/src/main/ets/pages/GraphPage.ets", line: 196, col: 17 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
detail: this.selectedNodeDetail,
onClose: (): void => { this.closeNodeDetail(); }
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
detail: this.selectedNodeDetail
});
}
}, { name: "NodeDetailPanel" });
}
});
}
else {
this.ifElseBranchUpdateFunction(1, () => {
});
}
}, If);
If.pop();
Stack.pop();
}
rerender() {
this.updateDirtyElements();
}
}

View File

@ -0,0 +1,25 @@
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/Index.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/Index.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/constant/TrulyMEMConstants.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/constant/TrulyMEMConstants.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/component/ImmersiveTabNavigation.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/component/ImmersiveTabNavigation.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/routermanager/PageContext.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/routermanager/PageContext.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/service/AIAgentService.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/service/AIAgentService.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/service/GraphMemoryService.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/service/GraphMemoryService.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/util/BreakpointSystem.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/util/BreakpointSystem.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/util/Logger.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/util/Logger.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/viewmodel/BaseViewModel.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/viewmodel/BaseViewModel.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/model/GraphDatabase.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/common/src/main/ets/model/GraphDatabase.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/graph/Index.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/graph/Index.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/graph/src/main/ets/pages/GraphPage.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/graph/src/main/ets/pages/GraphPage.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/graph/src/main/ets/components/GraphComponents.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/graph/src/main/ets/components/GraphComponents.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/chat/Index.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/chat/Index.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/chat/src/main/ets/pages/ChatPage.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/chat/src/main/ets/pages/ChatPage.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/chat/src/main/ets/components/ChatComponents.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/chat/src/main/ets/components/ChatComponents.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/settings/Index.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/settings/Index.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/settings/src/main/ets/components/SettingsComponents.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/settings/src/main/ets/components/SettingsComponents.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/settings/src/main/ets/pages/SettingsPage.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/settings/src/main/ets/pages/SettingsPage.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/phone/src/main/ets/pages/MainPage.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/phone/src/main/ets/pages/MainPage.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/phone/build/default/generated/r/default/ResourceTable.js;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/phone/build/default/generated/r/default/ResourceTable.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/phone/src/main/ets/entryability/EntryAbility.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/phone/src/main/ets/entryability/EntryAbility.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/phone/src/main/ets/pages/SplashPage.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/phone/src/main/ets/pages/SplashPage.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/phone/src/main/ets/pages/Index.ts;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/phone/src/main/ets/pages/Index.protoBin
/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/npmEntries.txt;/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/cache/default/default@CompileArkTS/esmodule/debug/npmEntries.protoBin

View File

@ -0,0 +1,7 @@
@system.app:@native.system.app
@ohos.app:@native.ohos.app
@system.router:@native.system.router
@system.curves:@native.system.curves
@ohos.curves:@native.ohos.curves
@system.matrix4:@native.system.matrix4
@ohos.matrix4:@native.ohos.matrix4

View File

@ -0,0 +1,2 @@
"use strict";
//# sourceMappingURL=ResourceTable.js.map

View File

@ -0,0 +1,49 @@
import UIAbility from "@ohos:app.ability.UIAbility";
import type AbilityConstant from "@ohos:app.ability.AbilityConstant";
import type Want from "@ohos:app.ability.Want";
import type window from "@ohos:window";
import type { BusinessError } from "@ohos:base";
import { defaultLogger } from "@normalized:N&&&@ohos/common/Index&1.0.0";
export default class EntryAbility extends UIAbility {
onCreate(want: Want, param: AbilityConstant.LaunchParam): void {
defaultLogger.info('EntryAbility onCreate');
}
onDestroy(): void {
defaultLogger.info('EntryAbility onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
const windowClass: window.Window = windowStage.getMainWindowSync();
try {
windowClass.setWindowBackgroundColor('#00000000');
}
catch (e) {
defaultLogger.error('Failed to set background color: ' + (e as BusinessError).message);
}
try {
windowClass.setWindowSystemBarProperties({
statusBarColor: '#00000000',
navigationBarColor: '#00000000'
});
}
catch (e) {
defaultLogger.error('Failed to set system bar properties: ' + (e as BusinessError).message);
}
defaultLogger.info('EntryAbility onWindowStageCreate');
windowStage.loadContent('pages/SplashPage', (err, data) => {
if (err.code) {
defaultLogger.error('Failed to load the content. Cause: ' + JSON.stringify(err));
return;
}
defaultLogger.info('Succeeded in loading the content. Data: ' + JSON.stringify(data));
});
}
onWindowStageDestroy(): void {
defaultLogger.info('EntryAbility onWindowStageDestroy');
}
onForeground(): void {
defaultLogger.info('EntryAbility onForeground');
}
onBackground(): void {
defaultLogger.info('EntryAbility onBackground');
}
}

View File

@ -0,0 +1,99 @@
if (!("finalizeConstruction" in ViewPU.prototype)) {
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface Index_Params {
db?: GraphDatabase;
displayCallback?: Callback<number>;
}
import { GraphDatabase } from "@normalized:N&&&@ohos/common/Index&1.0.0";
import { MainPage } from "@normalized:N&&&@ohos/phone/src/main/ets/pages/MainPage&";
import display from "@ohos:display";
class Index extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.db = new GraphDatabase();
this.displayCallback = undefined;
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: Index_Params) {
if (params.db !== undefined) {
this.db = params.db;
}
if (params.displayCallback !== undefined) {
this.displayCallback = params.displayCallback;
}
}
updateStateVars(params: Index_Params) {
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
}
aboutToBeDeleted() {
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private db: GraphDatabase;
private displayCallback?: Callback<number>;
aboutToAppear() {
const ctx = this.getUIContext()?.getHostContext();
if (ctx) {
this.db.init(ctx);
}
// TODO: display.on('change') API 在 API 23 中已废弃,应替换为窗口尺寸监听
// 当前保留以兼容旧代码,后续应使用 window.on('windowSizeChange') 或断点系统替代
// try {
// this.displayCallback = (size: number): void => { };
// display.on('change', this.displayCallback);
// } catch (e) {
// Logger.error('display.on error: ' + JSON.stringify(e));
// }
}
aboutToDisappear() {
if (this.displayCallback) {
display.off('change', this.displayCallback);
}
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.width('100%');
Column.height('100%');
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
__Common__.create();
__Common__.width('100%');
__Common__.height('100%');
}, __Common__);
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new MainPage(this, { db: this.db }, undefined, elmtId, () => { }, { page: "products/phone/src/main/ets/pages/Index.ets", line: 34, col: 7 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
db: this.db
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
db: this.db
});
}
}, { name: "MainPage" });
}
__Common__.pop();
Column.pop();
}
rerender() {
this.updateDirtyElements();
}
static getEntryName(): string {
return "Index";
}
}
registerNamedRoute(() => new Index(undefined, {}), "", { bundleName: "com.trulymem.app", moduleName: "phone", pagePath: "pages/Index", pageFullPath: "products/phone/src/main/ets/pages/Index", integratedHsp: "false", moduleType: "followWithHap" });

View File

@ -0,0 +1,325 @@
if (!("finalizeConstruction" in ViewPU.prototype)) {
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface MainPage_Params {
db?: GraphDatabase;
isWide?: boolean;
currentIndex?: number;
}
import { GraphDatabase, Logger, ImmersiveTabNavigation } from "@normalized:N&&&@ohos/common/Index&1.0.0";
import { GraphPage } from "@normalized:N&&&@ohos/graph/Index&1.0.0";
import { ChatPage } from "@normalized:N&&&@ohos/chat/Index&1.0.0";
import { SettingsPage } from "@normalized:N&&&@ohos/settings/Index&1.0.0";
import display from "@ohos:display";
export function MainPageBuilder(parent = null) {
{
(parent ? parent : this).observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new MainPage(parent ? parent : this, { db: new GraphDatabase() }, undefined, elmtId, () => { }, { page: "products/phone/src/main/ets/pages/MainPage.ets", line: 10, col: 3 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
db: new GraphDatabase()
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
(parent ? parent : this).updateStateVarsOfChildByElmtId(elmtId, {
db: new GraphDatabase()
});
}
}, { name: "MainPage" });
}
}
export class MainPage extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.__db = new SynchedPropertyObjectOneWayPU(params.db, this, "db");
this.__isWide = new ObservedPropertySimplePU(false, this, "isWide");
this.__currentIndex = new ObservedPropertySimplePU(0, this, "currentIndex");
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: MainPage_Params) {
if (params.isWide !== undefined) {
this.isWide = params.isWide;
}
if (params.currentIndex !== undefined) {
this.currentIndex = params.currentIndex;
}
}
updateStateVars(params: MainPage_Params) {
this.__db.reset(params.db);
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__db.purgeDependencyOnElmtId(rmElmtId);
this.__isWide.purgeDependencyOnElmtId(rmElmtId);
this.__currentIndex.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__db.aboutToBeDeleted();
this.__isWide.aboutToBeDeleted();
this.__currentIndex.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private __db: SynchedPropertySimpleOneWayPU<GraphDatabase>;
get db() {
return this.__db.get();
}
set db(newValue: GraphDatabase) {
this.__db.set(newValue);
}
private __isWide: ObservedPropertySimplePU<boolean>;
get isWide() {
return this.__isWide.get();
}
set isWide(newValue: boolean) {
this.__isWide.set(newValue);
}
private __currentIndex: ObservedPropertySimplePU<number>;
get currentIndex() {
return this.__currentIndex.get();
}
set currentIndex(newValue: number) {
this.__currentIndex.set(newValue);
}
aboutToAppear() {
this.updateBreakpoint();
try {
display.on('change', () => { this.updateBreakpoint(); });
}
catch (e) {
Logger.error('display.on error: ' + JSON.stringify(e));
}
}
private updateBreakpoint(): void {
try {
const defaultWindow = display.getDefaultDisplaySync();
this.isWide = defaultWindow.width > 520;
}
catch (e) {
Logger.error('updateBreakpoint error: ' + JSON.stringify(e));
}
}
TrulyMEMContent(parent = null) {
this.observeComponentCreation2((elmtId, isInitialRender) => {
If.create();
if (this.isWide) {
this.ifElseBranchUpdateFunction(0, () => {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Row.create();
Row.width('100%');
Row.height('100%');
Row.padding(4);
Row.backgroundColor('#1A1B2E');
}, Row);
this.observeComponentCreation2((elmtId, isInitialRender) => {
__Common__.create();
__Common__.layoutWeight(1);
__Common__.height('100%');
__Common__.clip(true);
__Common__.borderRadius(12);
__Common__.margin({ left: 4, right: 2 });
}, __Common__);
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new GraphPage(this, { db: this.db }, undefined, elmtId, () => { }, { page: "products/phone/src/main/ets/pages/MainPage.ets", line: 41, col: 9 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
db: this.db
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
db: this.db
});
}
}, { name: "GraphPage" });
}
__Common__.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
__Common__.create();
__Common__.width(380);
__Common__.height('100%');
__Common__.clip(true);
__Common__.borderRadius(12);
__Common__.margin({ left: 2, right: 4 });
}, __Common__);
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new ChatPage(this, { db: this.db }, undefined, elmtId, () => { }, { page: "products/phone/src/main/ets/pages/MainPage.ets", line: 48, col: 9 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
db: this.db
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
db: this.db
});
}
}, { name: "ChatPage" });
}
__Common__.pop();
Row.pop();
});
}
else {
this.ifElseBranchUpdateFunction(1, () => {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.width('100%');
Column.height('100%');
Column.padding(2);
Column.backgroundColor('#1A1B2E');
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Stack.create();
Stack.height('55%');
Stack.width('100%');
Stack.clip(true);
Stack.borderRadius(12);
Stack.margin({ top: 2, left: 4, right: 4, bottom: 2 });
}, Stack);
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new GraphPage(this, { db: this.db }, undefined, elmtId, () => { }, { page: "products/phone/src/main/ets/pages/MainPage.ets", line: 61, col: 19 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
db: this.db
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
db: this.db
});
}
}, { name: "GraphPage" });
}
Stack.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Stack.create();
Stack.height('45%');
Stack.width('100%');
Stack.clip(true);
Stack.borderRadius(12);
Stack.margin({ top: 2, left: 4, right: 4, bottom: 2 });
}, Stack);
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new ChatPage(this, { db: this.db }, undefined, elmtId, () => { }, { page: "products/phone/src/main/ets/pages/MainPage.ets", line: 68, col: 19 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
db: this.db
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
db: this.db
});
}
}, { name: "ChatPage" });
}
Stack.pop();
Column.pop();
});
}
}, If);
If.pop();
}
SettingsContent(parent = null) {
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new SettingsPage(this, {}, undefined, elmtId, () => { }, { page: "products/phone/src/main/ets/pages/MainPage.ets", line: 84, col: 5 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {});
}
}, { name: "SettingsPage" });
}
}
TabContentBuilder(parent = null) {
this.observeComponentCreation2((elmtId, isInitialRender) => {
If.create();
if (this.currentIndex === 0) {
this.ifElseBranchUpdateFunction(0, () => {
this.TrulyMEMContent.bind(this)();
});
}
else {
this.ifElseBranchUpdateFunction(1, () => {
this.SettingsContent.bind(this)();
});
}
}, If);
If.pop();
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
__Common__.create();
__Common__.width('100%');
__Common__.height('100%');
}, __Common__);
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new ImmersiveTabNavigation(this, {
contentBuilder: this.TabContentBuilder,
onTabChange: (index: number) => {
this.currentIndex = index;
}
}, undefined, elmtId, () => { }, { page: "products/phone/src/main/ets/pages/MainPage.ets", line: 97, col: 5 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
contentBuilder: this.TabContentBuilder,
onTabChange: (index: number) => {
this.currentIndex = index;
}
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {});
}
}, { name: "ImmersiveTabNavigation" });
}
__Common__.pop();
}
rerender() {
this.updateDirtyElements();
}
}
(function () {
if (typeof NavigationBuilderRegister === "function") {
NavigationBuilderRegister("MainPage", wrapBuilder(MainPageBuilder));
}
})();

View File

@ -0,0 +1,66 @@
if (!("finalizeConstruction" in ViewPU.prototype)) {
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface SplashPage_Params {
}
class SplashPage extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: SplashPage_Params) {
}
updateStateVars(params: SplashPage_Params) {
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
}
aboutToBeDeleted() {
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
aboutToAppear() {
setTimeout(() => {
const uiContext = this.getUIContext();
if (uiContext) {
const router = uiContext.getRouter();
router.replaceUrl({ url: 'pages/Index' });
}
}, 2000);
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.width('100%');
Column.height('100%');
Column.backgroundColor('#1A1B2E');
Column.justifyContent(FlexAlign.Center);
Column.alignItems(HorizontalAlign.Center);
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create('TrulyMEM');
Text.fontSize(48);
Text.fontWeight(FontWeight.Bold);
Text.fontColor(Color.White);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create('True Human Memory');
Text.fontSize(20);
Text.fontColor(Color.White);
Text.margin({ top: 16 });
}, Text);
Text.pop();
Column.pop();
}
rerender() {
this.updateDirtyElements();
}
static getEntryName(): string {
return "SplashPage";
}
}
registerNamedRoute(() => new SplashPage(undefined, {}), "", { bundleName: "com.trulymem.app", moduleName: "phone", pagePath: "pages/SplashPage", pageFullPath: "products/phone/src/main/ets/pages/SplashPage", integratedHsp: "false", moduleType: "followWithHap" });

View File

@ -0,0 +1 @@
export { SettingsPage } from "@normalized:N&&&@ohos/settings/src/main/ets/pages/SettingsPage&1.0.0";

View File

@ -0,0 +1,245 @@
if (!("finalizeConstruction" in ViewPU.prototype)) {
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface GlowBackground_Params {
color?: string;
glowSize?: number;
}
interface SettingInputItem_Params {
label?: string;
placeholder?: string;
value?: string;
isPassword?: boolean;
onValueChange?: (value: string) => void;
}
interface SettingsSectionHeader_Params {
title?: string;
}
import dataPreferences from "@ohos:data.preferences";
import type common from "@ohos:app.ability.common";
export class SettingsSectionHeader extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.__title = new SynchedPropertySimpleOneWayPU(params.title, this, "title");
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: SettingsSectionHeader_Params) {
}
updateStateVars(params: SettingsSectionHeader_Params) {
this.__title.reset(params.title);
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__title.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__title.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private __title: SynchedPropertySimpleOneWayPU<string>;
get title() {
return this.__title.get();
}
set title(newValue: string) {
this.__title.set(newValue);
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(this.title);
Text.fontSize(24);
Text.fontWeight(FontWeight.Bold);
Text.fontColor('#FFFFFF');
Text.margin({ top: 20, bottom: 16 });
}, Text);
Text.pop();
}
rerender() {
this.updateDirtyElements();
}
}
export class SettingInputItem extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.__label = new SynchedPropertySimpleOneWayPU(params.label, this, "label");
this.__placeholder = new SynchedPropertySimpleOneWayPU(params.placeholder, this, "placeholder");
this.__value = new SynchedPropertySimpleTwoWayPU(params.value, this, "value");
this.isPassword = false;
this.onValueChange = undefined;
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: SettingInputItem_Params) {
if (params.isPassword !== undefined) {
this.isPassword = params.isPassword;
}
if (params.onValueChange !== undefined) {
this.onValueChange = params.onValueChange;
}
}
updateStateVars(params: SettingInputItem_Params) {
this.__label.reset(params.label);
this.__placeholder.reset(params.placeholder);
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__label.purgeDependencyOnElmtId(rmElmtId);
this.__placeholder.purgeDependencyOnElmtId(rmElmtId);
this.__value.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__label.aboutToBeDeleted();
this.__placeholder.aboutToBeDeleted();
this.__value.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private __label: SynchedPropertySimpleOneWayPU<string>;
get label() {
return this.__label.get();
}
set label(newValue: string) {
this.__label.set(newValue);
}
private __placeholder: SynchedPropertySimpleOneWayPU<string>;
get placeholder() {
return this.__placeholder.get();
}
set placeholder(newValue: string) {
this.__placeholder.set(newValue);
}
private __value: SynchedPropertySimpleTwoWayPU<string>;
get value() {
return this.__value.get();
}
set value(newValue: string) {
this.__value.set(newValue);
}
private isPassword?: boolean;
private onValueChange?: (value: string) => void;
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.padding(12);
Column.backgroundColor('rgba(255,255,255,0.05)');
Column.borderRadius(12);
Column.backgroundBlurStyle(BlurStyle.Thin);
Column.margin({ bottom: 12 });
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(this.label);
Text.fontSize(14);
Text.fontColor('#FFFFFF');
Text.width('100%');
Text.margin({ bottom: 8 });
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
TextInput.create({ placeholder: this.placeholder, text: this.value });
TextInput.type(this.isPassword ? InputType.Password : InputType.Normal);
TextInput.onChange((v: string) => {
this.value = v;
this.onValueChange?.(v);
});
TextInput.backgroundColor('rgba(255,255,255,0.1)');
TextInput.borderRadius(8);
TextInput.border({ width: 1, color: 'rgba(124,77,255,0.3)' });
TextInput.height(40);
}, TextInput);
Column.pop();
}
rerender() {
this.updateDirtyElements();
}
}
export class GlowBackground extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.__color = new SynchedPropertySimpleOneWayPU(params.color, this, "color");
this.__glowSize = new SynchedPropertySimpleOneWayPU(params.glowSize, this, "glowSize");
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: GlowBackground_Params) {
if (params.color === undefined) {
this.__color.set('rgba(124,77,255,0.15)');
}
if (params.glowSize === undefined) {
this.__glowSize.set(200);
}
}
updateStateVars(params: GlowBackground_Params) {
this.__color.reset(params.color);
this.__glowSize.reset(params.glowSize);
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__color.purgeDependencyOnElmtId(rmElmtId);
this.__glowSize.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__color.aboutToBeDeleted();
this.__glowSize.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private __color: SynchedPropertySimpleOneWayPU<string>;
get color() {
return this.__color.get();
}
set color(newValue: string) {
this.__color.set(newValue);
}
private __glowSize: SynchedPropertySimpleOneWayPU<number>;
get glowSize() {
return this.__glowSize.get();
}
set glowSize(newValue: number) {
this.__glowSize.set(newValue);
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.width(this.glowSize);
Column.height(this.glowSize);
Column.backgroundColor(this.color);
Column.blur(40);
Column.borderRadius(this.glowSize / 2);
Column.position({ x: '10%', y: '20%' });
}, Column);
Column.pop();
}
rerender() {
this.updateDirtyElements();
}
}
/**
* AppConfigStore — 应用配置存储封装
* 封装 Preferences 读写,提供类型安全访问
*/
export class AppConfigStore {
private pref?: dataPreferences.Preferences;
private readonly storeName: string = 'trulymem_config';
async init(ctx: common.Context): Promise<void> {
this.pref = await dataPreferences.getPreferences(ctx, this.storeName);
}
async getString(key: string, defaultValue: string): Promise<string> {
return String(await this.pref?.get(key, defaultValue));
}
async setString(key: string, value: string): Promise<void> {
await this.pref?.put(key, value);
await this.pref?.flush();
}
static async create(ctx: common.Context): Promise<AppConfigStore> {
const store = new AppConfigStore();
await store.init(ctx);
return store;
}
}

View File

@ -0,0 +1,223 @@
if (!("finalizeConstruction" in ViewPU.prototype)) {
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface SettingsPage_Params {
baseUrl?: string;
model?: string;
apiKey?: string;
store?: AppConfigStore;
}
import { SettingsSectionHeader, SettingInputItem, GlowBackground, AppConfigStore } from "@normalized:N&&&@ohos/settings/src/main/ets/components/SettingsComponents&1.0.0";
export class SettingsPage extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.__baseUrl = new ObservedPropertySimplePU('', this, "baseUrl");
this.__model = new ObservedPropertySimplePU('', this, "model");
this.__apiKey = new ObservedPropertySimplePU('', this, "apiKey");
this.store = new AppConfigStore();
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: SettingsPage_Params) {
if (params.baseUrl !== undefined) {
this.baseUrl = params.baseUrl;
}
if (params.model !== undefined) {
this.model = params.model;
}
if (params.apiKey !== undefined) {
this.apiKey = params.apiKey;
}
if (params.store !== undefined) {
this.store = params.store;
}
}
updateStateVars(params: SettingsPage_Params) {
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__baseUrl.purgeDependencyOnElmtId(rmElmtId);
this.__model.purgeDependencyOnElmtId(rmElmtId);
this.__apiKey.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__baseUrl.aboutToBeDeleted();
this.__model.aboutToBeDeleted();
this.__apiKey.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
private __baseUrl: ObservedPropertySimplePU<string>;
get baseUrl() {
return this.__baseUrl.get();
}
set baseUrl(newValue: string) {
this.__baseUrl.set(newValue);
}
private __model: ObservedPropertySimplePU<string>;
get model() {
return this.__model.get();
}
set model(newValue: string) {
this.__model.set(newValue);
}
private __apiKey: ObservedPropertySimplePU<string>;
get apiKey() {
return this.__apiKey.get();
}
set apiKey(newValue: string) {
this.__apiKey.set(newValue);
}
private store: AppConfigStore;
async aboutToAppear() {
const ctx = getContext(this);
await this.store.init(ctx);
this.baseUrl = await this.store.getString('base_url', 'https://api.deepseek.com');
this.model = await this.store.getString('model', 'deepseek-chat');
this.apiKey = await this.store.getString('api_key', '');
}
private async saveConfig(key: string, value: string): Promise<void> {
await this.store.setString(key, value);
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Stack.create();
Stack.width('100%');
Stack.height('100%');
Stack.backgroundColor('rgba(26,27,46,0.95)');
Stack.backgroundBlurStyle(BlurStyle.Regular);
}, Stack);
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new GlowBackground(this, {}, undefined, elmtId, () => { }, { page: "features/settings/src/main/ets/pages/SettingsPage.ets", line: 25, col: 13 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {});
}
}, { name: "GlowBackground" });
}
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.padding(16);
Column.width('100%');
}, Column);
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new SettingsSectionHeader(this, { title: 'API 配置' }, undefined, elmtId, () => { }, { page: "features/settings/src/main/ets/pages/SettingsPage.ets", line: 28, col: 17 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
title: 'API 配置'
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
title: 'API 配置'
});
}
}, { name: "SettingsSectionHeader" });
}
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new SettingInputItem(this, {
label: 'Base URL',
placeholder: 'https://api.deepseek.com',
value: this.__baseUrl,
onValueChange: (v: string): void => { this.saveConfig('base_url', v); }
}, undefined, elmtId, () => { }, { page: "features/settings/src/main/ets/pages/SettingsPage.ets", line: 30, col: 17 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
label: 'Base URL',
placeholder: 'https://api.deepseek.com',
value: this.baseUrl,
onValueChange: (v: string): void => { this.saveConfig('base_url', v); }
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
label: 'Base URL',
placeholder: 'https://api.deepseek.com'
});
}
}, { name: "SettingInputItem" });
}
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new SettingInputItem(this, {
label: 'Model ID',
placeholder: 'deepseek-chat',
value: this.__model,
onValueChange: (v: string): void => { this.saveConfig('model', v); }
}, undefined, elmtId, () => { }, { page: "features/settings/src/main/ets/pages/SettingsPage.ets", line: 37, col: 17 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
label: 'Model ID',
placeholder: 'deepseek-chat',
value: this.model,
onValueChange: (v: string): void => { this.saveConfig('model', v); }
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
label: 'Model ID',
placeholder: 'deepseek-chat'
});
}
}, { name: "SettingInputItem" });
}
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new SettingInputItem(this, {
label: 'API Key',
placeholder: 'sk-...',
value: this.__apiKey,
isPassword: true,
onValueChange: (v: string): void => { this.saveConfig('api_key', v); }
}, undefined, elmtId, () => { }, { page: "features/settings/src/main/ets/pages/SettingsPage.ets", line: 44, col: 17 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
label: 'API Key',
placeholder: 'sk-...',
value: this.apiKey,
isPassword: true,
onValueChange: (v: string): void => { this.saveConfig('api_key', v); }
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
label: 'API Key',
placeholder: 'sk-...'
});
}
}, { name: "SettingInputItem" });
}
Column.pop();
Stack.pop();
}
rerender() {
this.updateDirtyElements();
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,25 @@
/**
* Use these variables when you tailor your ArkTS code. They must be of the const type.
*/
export const BUNDLE_NAME = 'com.trulymem.app';
export const BUNDLE_TYPE = 'app';
export const VERSION_CODE = 1000001;
export const VERSION_NAME = '1.0.0';
export const TARGET_NAME = 'default';
export const PRODUCT_NAME = 'default';
export const BUILD_MODE_NAME = 'debug';
export const DEBUG = true;
/**
* BuildProfile Class is used only for compatibility purposes.
*/
export default class BuildProfile {
static readonly BUNDLE_NAME = BUNDLE_NAME;
static readonly BUNDLE_TYPE = BUNDLE_TYPE;
static readonly VERSION_CODE = VERSION_CODE;
static readonly VERSION_NAME = VERSION_NAME;
static readonly TARGET_NAME = TARGET_NAME;
static readonly PRODUCT_NAME = PRODUCT_NAME;
static readonly BUILD_MODE_NAME = BUILD_MODE_NAME;
static readonly DEBUG = DEBUG;
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RESOURCE_TABLE_H
#define RESOURCE_TABLE_H
#include<stdint.h>
namespace OHOS {
const int32_t STRING_ENTRYABILITY_DESC = 0x04000001;
const int32_t STRING_ENTRYABILITY_LABEL = 0x04000002;
const int32_t STRING_MAINABILITY_DESC = 0x04000003;
const int32_t STRING_MAINABILITY_LABEL = 0x04000004;
const int32_t STRING_APP_NAME = 0x04000005;
const int32_t STRING_MODULE_DESC = 0x04000006;
const int32_t COLOR_START_WINDOW_BACKGROUND = 0x04000007;
const int32_t MEDIA_LAYERED_IMAGE = 0x04000000;
const int32_t MEDIA_STARTICON = 0x04000008;
const int32_t PROFILE_MAIN_PAGES = 0x04000009;
const int32_t PROFILE_ROUTER_MAP = 0x0400000a;
}
#endif

View File

@ -0,0 +1,16 @@
/*
* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//@ts-noCheck

View File

@ -0,0 +1 @@
[{"hapName":"phone-default-unsigned.hap","deviceTypes":["phone","tablet","2in1"],"isSigned":false}]

View File

@ -0,0 +1,105 @@
{
"modulePathMap": {
"common": "/home/program/TrulyMEM-TrueHumanMEM/common",
"graph": "/home/program/TrulyMEM-TrueHumanMEM/features/graph",
"chat": "/home/program/TrulyMEM-TrueHumanMEM/features/chat",
"settings": "/home/program/TrulyMEM-TrueHumanMEM/features/settings",
"phone": "/home/program/TrulyMEM-TrueHumanMEM/products/phone"
},
"compileMode": "esmodule",
"projectRootPath": "/home/program/TrulyMEM-TrueHumanMEM",
"nodeModulesPath": "/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/intermediates/loader_out/default/node_modules",
"byteCodeHarInfo": {},
"declarationEntry": [],
"moduleName": "phone",
"hspNameOhmMap": {},
"harNameOhmMap": {},
"packageManagerType": "ohpm",
"compileEntry": [
"/home/program/TrulyMEM-TrueHumanMEM/common/Index.ets",
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/component/ImmersiveTabNavigation.ets",
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/constant/TrulyMEMConstants.ets",
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/model/GraphDatabase.ets",
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/routermanager/PageContext.ets",
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/service/AIAgentService.ets",
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/service/GraphMemoryService.ets",
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/util/BreakpointSystem.ets",
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/util/Logger.ets",
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/viewmodel/BaseViewModel.ets",
"/home/program/TrulyMEM-TrueHumanMEM/features/graph/Index.ets",
"/home/program/TrulyMEM-TrueHumanMEM/features/graph/src/main/ets/components/GraphComponents.ets",
"/home/program/TrulyMEM-TrueHumanMEM/features/graph/src/main/ets/pages/GraphPage.ets",
"/home/program/TrulyMEM-TrueHumanMEM/features/chat/Index.ets",
"/home/program/TrulyMEM-TrueHumanMEM/features/chat/src/main/ets/components/ChatComponents.ets",
"/home/program/TrulyMEM-TrueHumanMEM/features/chat/src/main/ets/pages/ChatPage.ets",
"/home/program/TrulyMEM-TrueHumanMEM/features/settings/Index.ets",
"/home/program/TrulyMEM-TrueHumanMEM/features/settings/src/main/ets/components/SettingsComponents.ets",
"/home/program/TrulyMEM-TrueHumanMEM/features/settings/src/main/ets/pages/SettingsPage.ets",
"/home/program/TrulyMEM-TrueHumanMEM/products/phone/src/main/ets/pages/MainPage.ets",
"/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/generated/r/default/ResourceTable.ts"
],
"otherCompileFiles": [],
"dynamicImportLibInfo": {
"@ohos/common": {
"hostModulesInfo": [
{
"hostDependencyName": "@ohos/common",
"hostModuleName": "phone"
}
],
"moduleName": "common",
"entryFilePath": "/home/program/TrulyMEM-TrueHumanMEM/common/Index.ets",
"isLocalDependency": true,
"pkgPath": "/home/program/TrulyMEM-TrueHumanMEM/common"
},
"@ohos/graph": {
"hostModulesInfo": [
{
"hostDependencyName": "@ohos/graph",
"hostModuleName": "phone"
}
],
"moduleName": "graph",
"entryFilePath": "/home/program/TrulyMEM-TrueHumanMEM/features/graph/Index.ets",
"isLocalDependency": true,
"pkgPath": "/home/program/TrulyMEM-TrueHumanMEM/features/graph"
},
"@ohos/chat": {
"hostModulesInfo": [
{
"hostDependencyName": "@ohos/chat",
"hostModuleName": "phone"
}
],
"moduleName": "chat",
"entryFilePath": "/home/program/TrulyMEM-TrueHumanMEM/features/chat/Index.ets",
"isLocalDependency": true,
"pkgPath": "/home/program/TrulyMEM-TrueHumanMEM/features/chat"
},
"@ohos/settings": {
"hostModulesInfo": [
{
"hostDependencyName": "@ohos/settings",
"hostModuleName": "phone"
}
],
"moduleName": "settings",
"entryFilePath": "/home/program/TrulyMEM-TrueHumanMEM/features/settings/Index.ets",
"isLocalDependency": true,
"pkgPath": "/home/program/TrulyMEM-TrueHumanMEM/features/settings"
}
},
"routerMap": [
{
"ohmurl": "@normalized:N&&&@ohos/phone/src/main/ets/pages/MainPage&",
"name": "MainPage",
"pageSourceFile": "/home/program/TrulyMEM-TrueHumanMEM/products/phone/src/main/ets/pages/MainPage.ets",
"buildFunction": "MainPageBuilder"
}
],
"hspResourcesMap": {},
"updateVersionInfo": {},
"customizedHar": false,
"anBuildOutPut": "/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/intermediates/loader_out/default/an/arm64-v8a",
"anBuildMode": "type"
}

View File

@ -0,0 +1 @@
{"@ohos/common":{"packageName":"@ohos/common","bundleName":"","moduleName":"","version":"1.0.0","entryPath":"Index.ets","isSO":false,"dependencyAlias":""},"@ohos/graph":{"packageName":"@ohos/graph","bundleName":"","moduleName":"","version":"1.0.0","entryPath":"Index.ets","isSO":false,"dependencyAlias":""},"@ohos/chat":{"packageName":"@ohos/chat","bundleName":"","moduleName":"","version":"1.0.0","entryPath":"Index.ets","isSO":false,"dependencyAlias":""},"@ohos/settings":{"packageName":"@ohos/settings","bundleName":"","moduleName":"","version":"1.0.0","entryPath":"Index.ets","isSO":false,"dependencyAlias":""},"@ohos/hypium":{"packageName":"@ohos/hypium","bundleName":"","moduleName":"","version":"1.0.24","entryPath":"index.js","isSO":false,"dependencyAlias":""},"@ohos/hamock":{"packageName":"@ohos/hamock","bundleName":"","moduleName":"","version":"1.0.0","entryPath":"index.ets","isSO":false,"dependencyAlias":""},"@ohos/phone":{"packageName":"@ohos/phone","bundleName":"","moduleName":"","version":"","entryPath":"src/main/","isSO":false,"dependencyAlias":""}}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,66 @@
{
"app": {
"bundleName": "com.trulymem.app",
"vendor": "trulymem",
"versionCode": 1000001,
"versionName": "1.0.0",
"icon": "$media:layered_image",
"label": "$string:app_name",
"apiReleaseType": "Release",
"compileSdkVersion": "6.1.0.105",
"targetAPIVersion": 60100023,
"minAPIVersion": 60100023,
"compileSdkType": "HarmonyOS",
"targetMinorAPIVersion": 0,
"targetPatchAPIVersion": 0,
"appEnvironments": [],
"bundleType": "app",
"buildMode": "debug",
"debug": true
},
"module": {
"name": "phone",
"type": "entry",
"description": "TrulyMEM phone entry module",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone",
"tablet",
"2in1"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"routerMap": "$profile:router_map",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"ohos.want.action.home"
]
}
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.GET_NETWORK_INFO"
}
],
"packageName": "@ohos/phone"
}
}

View File

@ -0,0 +1 @@
{"app":{"bundleName":"com.trulymem.app","vendor":"trulymem","versionCode":1000001,"versionName":"1.0.0","icon":"$media:layered_image","label":"$string:app_name","apiReleaseType":"Release","compileSdkVersion":"6.1.0.105","targetAPIVersion":60100023,"minAPIVersion":60100023,"compileSdkType":"HarmonyOS","targetMinorAPIVersion":0,"targetPatchAPIVersion":0,"appEnvironments":[],"bundleType":"app","buildMode":"debug","debug":true,"iconId":67108864,"labelId":67108869},"module":{"name":"phone","type":"entry","description":"TrulyMEM phone entry module","mainElement":"EntryAbility","deviceTypes":["phone","tablet","2in1"],"deliveryWithInstall":true,"installationFree":false,"pages":"$profile:main_pages","routerMap":"$profile:router_map","abilities":[{"name":"EntryAbility","srcEntry":"./ets/entryability/EntryAbility.ets","description":"$string:EntryAbility_desc","label":"$string:EntryAbility_label","startWindowIcon":"$media:startIcon","startWindowBackground":"$color:start_window_background","exported":true,"skills":[{"entities":["entity.system.home"],"actions":["ohos.want.action.home"]}],"descriptionId":67108865,"labelId":67108866,"startWindowIconId":67108872,"startWindowBackgroundId":67108871}],"requestPermissions":[{"name":"ohos.permission.INTERNET"},{"name":"ohos.permission.GET_NETWORK_INFO"}],"packageName":"@ohos/phone","virtualMachine":"ark13.0.1.0","compileMode":"esmodule","dependencies":[]}}

View File

@ -0,0 +1 @@
{"libs":{},"stripped":{}}

View File

@ -0,0 +1 @@
{"libs":{},"binxo":{},"binxoSymbol":{"enableAsanBinxo":false,"excludeSoFromBinxo":[]}}

View File

@ -0,0 +1,69 @@
{
"app": {
"bundleName": "com.trulymem.app",
"vendor": "trulymem",
"versionCode": 1000001,
"versionName": "1.0.0",
"icon": "$media:layered_image",
"label": "$string:app_name",
"apiReleaseType": "Release",
"compileSdkVersion": "6.1.0.105",
"targetAPIVersion": 60100023,
"minAPIVersion": 60100023,
"compileSdkType": "HarmonyOS",
"targetMinorAPIVersion": 0,
"targetPatchAPIVersion": 0,
"appEnvironments": [],
"bundleType": "app",
"buildMode": "debug",
"debug": true
},
"module": {
"name": "phone",
"type": "entry",
"description": "TrulyMEM phone entry module",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone",
"tablet",
"2in1"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"routerMap": "$profile:router_map",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"ohos.want.action.home"
]
}
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.GET_NETWORK_INFO"
}
],
"packageName": "@ohos/phone",
"virtualMachine": "ark13.0.1.0",
"compileMode": "esmodule",
"dependencies": []
}
}

View File

@ -0,0 +1,11 @@
string EntryAbility_desc 0x04000001
string EntryAbility_label 0x04000002
string MainAbility_desc 0x04000003
string MainAbility_label 0x04000004
string app_name 0x04000005
string module_desc 0x04000006
color start_window_background 0x04000007
media layered_image 0x04000000
media startIcon 0x04000008
profile main_pages 0x04000009
profile router_map 0x0400000a

View File

@ -0,0 +1 @@
{"app":{"bundleName":"com.trulymem.app","vendor":"trulymem","versionCode":1000001,"versionName":"1.0.0","icon":"$media:layered_image","label":"$string:app_name","apiReleaseType":"Release","compileSdkVersion":"6.1.0.105","targetAPIVersion":60100023,"minAPIVersion":60100023,"compileSdkType":"HarmonyOS","targetMinorAPIVersion":0,"targetPatchAPIVersion":0,"appEnvironments":[],"bundleType":"app","buildMode":"debug","debug":true},"module":{"name":"phone","type":"entry","description":"TrulyMEM phone entry module","mainElement":"EntryAbility","deviceTypes":["phone","tablet","2in1"],"deliveryWithInstall":true,"installationFree":false,"pages":"$profile:main_pages","routerMap":"$profile:router_map","abilities":[{"name":"EntryAbility","srcEntry":"./ets/entryability/EntryAbility.ets","description":"$string:EntryAbility_desc","label":"$string:EntryAbility_label","startWindowIcon":"$media:startIcon","startWindowBackground":"$color:start_window_background","exported":true,"skills":[{"entities":["entity.system.home"],"actions":["ohos.want.action.home"]}]}],"requestPermissions":[{"name":"ohos.permission.INTERNET"},{"name":"ohos.permission.GET_NETWORK_INFO"}],"packageName":"@ohos/phone","virtualMachine":"ark13.0.1.0","compileMode":"esmodule","dependencies":[]}}

Some files were not shown because too many files have changed in this diff Show More