@pheem49/mint 1.5.5 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex +0 -0
- package/.github/FUNDING.yml +2 -0
- package/.github/workflows/ci.yml +45 -0
- package/.github/workflows/release.yml +79 -0
- package/Cargo.lock +5792 -0
- package/Cargo.toml +32 -0
- package/README.md +387 -353
- package/assets/icon.png +0 -0
- package/bin/mint +0 -0
- package/crates/mint-cli/Cargo.toml +23 -0
- package/crates/mint-cli/src/agent.rs +851 -0
- package/crates/mint-cli/src/gmail.rs +216 -0
- package/crates/mint-cli/src/image.rs +142 -0
- package/crates/mint-cli/src/main.rs +2837 -0
- package/crates/mint-cli/src/mcp.rs +63 -0
- package/crates/mint-cli/src/onboard.rs +1149 -0
- package/crates/mint-cli/src/setup.rs +390 -0
- package/crates/mint-cli/src/skills.rs +8 -0
- package/crates/mint-cli/src/updater.rs +279 -0
- package/crates/mint-core/Cargo.toml +22 -0
- package/crates/mint-core/src/agent_loop.rs +94 -0
- package/crates/mint-core/src/api_server.rs +991 -0
- package/crates/mint-core/src/channels.rs +248 -0
- package/crates/mint-core/src/chat.rs +895 -0
- package/crates/mint-core/src/code_tools.rs +729 -0
- package/crates/mint-core/src/config.rs +368 -0
- package/crates/mint-core/src/files.rs +159 -0
- package/crates/mint-core/src/knowledge.rs +541 -0
- package/crates/mint-core/src/lib.rs +84 -0
- package/crates/mint-core/src/mcp.rs +273 -0
- package/crates/mint-core/src/memory.rs +673 -0
- package/crates/mint-core/src/orchestration.rs +2157 -0
- package/crates/mint-core/src/pictures.rs +314 -0
- package/crates/mint-core/src/plugins.rs +727 -0
- package/crates/mint-core/src/safety.rs +416 -0
- package/crates/mint-core/src/semantic.rs +254 -0
- package/crates/mint-core/src/shell.rs +317 -0
- package/crates/mint-core/src/skills.rs +71 -0
- package/crates/mint-core/src/symbols.rs +157 -0
- package/crates/mint-core/src/tasks.rs +308 -0
- package/crates/mint-core/src/tts.rs +92 -0
- package/crates/mint-core/src/weather.rs +93 -0
- package/crates/mint-core/src/web_search.rs +200 -0
- package/crates/mint-core/src/workflows.rs +81 -0
- package/crates/mint-core/tests/mcp_stdio.rs +45 -0
- package/crates/mint-core/tests/memory_persistence.rs +172 -0
- package/crates/mint-core/tests/pictures_storage.rs +14 -0
- package/crates/mint-core/tests/task_lifecycle.rs +87 -0
- package/package.json +35 -99
- package/src/bin/index.js +16 -0
- package/src/renderer/index-web.html +17 -0
- package/src/renderer/index.html +17 -0
- package/src/renderer/public/Live2DCubismCore.js +9 -0
- package/src/renderer/public/assets/icon.png +0 -0
- package/src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.model3.json +36 -0
- package/src/renderer/src/App.tsx +33 -0
- package/src/renderer/src/calculator.ts +47 -0
- package/src/renderer/src/components/ChatPanel.tsx +1598 -0
- package/src/renderer/src/components/DashboardSidebar.tsx +358 -0
- package/src/renderer/src/components/Live2DStage.tsx +374 -0
- package/src/renderer/src/components/MintDashboard.tsx +950 -0
- package/src/renderer/src/components/ModelPanel.tsx +154 -0
- package/src/renderer/src/components/PicturesLibrary.tsx +46 -0
- package/src/renderer/src/components/ProactiveGlow.tsx +19 -0
- package/src/renderer/src/components/ScreenPicker.tsx +579 -0
- package/src/renderer/src/components/SettingsWindow.tsx +1467 -0
- package/src/renderer/src/components/SpotlightWindow.tsx +280 -0
- package/src/renderer/src/components/WidgetWindow.tsx +36 -0
- package/src/renderer/src/components/WorkspacePanel.tsx +268 -0
- package/src/{UI → renderer/src/css}/settings.css +69 -16
- package/src/renderer/src/css/spotlight.css +113 -0
- package/src/renderer/src/css/styles.css +3722 -0
- package/src/renderer/src/css/widget.css +185 -0
- package/src/renderer/src/env.d.ts +116 -0
- package/src/renderer/src/index.css +379 -0
- package/src/renderer/src/main.tsx +13 -0
- package/src/renderer/src/tauri.ts +996 -0
- package/src/renderer/src-web/App.tsx +25 -0
- package/src/renderer/src-web/calculator.ts +47 -0
- package/src/renderer/src-web/components/ChatPanel.tsx +1662 -0
- package/src/renderer/src-web/components/DashboardSidebar.tsx +242 -0
- package/src/renderer/src-web/components/MintDashboard.tsx +763 -0
- package/src/renderer/src-web/components/PicturesLibrary.tsx +73 -0
- package/src/renderer/src-web/components/SettingsWindow.tsx +1500 -0
- package/src/renderer/src-web/css/settings.css +1100 -0
- package/src/{UI → renderer/src-web/css}/spotlight.css +4 -4
- package/src/{UI → renderer/src-web/css}/styles.css +1055 -159
- package/src/{UI → renderer/src-web/css}/widget.css +2 -2
- package/src/renderer/src-web/env.d.ts +107 -0
- package/src/renderer/src-web/index.css +379 -0
- package/src/renderer/src-web/main.tsx +13 -0
- package/src/renderer/src-web/tauri.ts +983 -0
- package/tsconfig.json +30 -0
- package/vite.config.ts +33 -0
- package/vite.config.web.ts +51 -0
- package/GUIDE_TH.md +0 -125
- package/assets/Agent_Mint.png +0 -0
- package/assets/CLI_Screen.png +0 -0
- package/assets/Settings.png +0 -0
- package/benchmark_ai.js +0 -71
- package/install.ps1 +0 -64
- package/install.sh +0 -54
- package/main.js +0 -139
- package/mint-cli-logic.js +0 -3
- package/mint-cli.js +0 -410
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.model3.json +0 -47
- package/models/Shiroko_Model/Shiroko//342/232/241/351/253/230/344/272/256/342/232/241/344/275/277/347/224/250/346/225/231/347/250/213/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.txt +0 -23
- package/preload-picker.js +0 -11
- package/preload-settings.js +0 -11
- package/preload.js +0 -41
- package/scripts/install_linux_desktop_entry.js +0 -48
- package/src/AI_Brain/Gemini_API.js +0 -813
- package/src/AI_Brain/agent_orchestrator.js +0 -73
- package/src/AI_Brain/autonomous_brain.js +0 -179
- package/src/AI_Brain/behavior_memory.js +0 -135
- package/src/AI_Brain/headless_agent.js +0 -143
- package/src/AI_Brain/knowledge_base.js +0 -349
- package/src/AI_Brain/memory_store.js +0 -662
- package/src/AI_Brain/proactive_engine.js +0 -172
- package/src/AI_Brain/provider_adapter.js +0 -365
- package/src/Automation_Layer/browser_automation.js +0 -149
- package/src/Automation_Layer/file_operations.js +0 -286
- package/src/Automation_Layer/open_app.js +0 -85
- package/src/Automation_Layer/open_website.js +0 -38
- package/src/CLI/approval_handler.js +0 -47
- package/src/CLI/chat_router.js +0 -247
- package/src/CLI/chat_ui.js +0 -1159
- package/src/CLI/cli_colors.js +0 -115
- package/src/CLI/cli_formatters.js +0 -94
- package/src/CLI/code_agent.js +0 -1667
- package/src/CLI/code_session_memory.js +0 -62
- package/src/CLI/gmail_auth.js +0 -210
- package/src/CLI/image_input.js +0 -90
- package/src/CLI/intent_detectors.js +0 -181
- package/src/CLI/interactive_chat.js +0 -658
- package/src/CLI/list_features.js +0 -64
- package/src/CLI/onboarding.js +0 -416
- package/src/CLI/repo_summarizer.js +0 -282
- package/src/CLI/semantic_code_search.js +0 -312
- package/src/CLI/skill_manager.js +0 -41
- package/src/CLI/slash_command_handler.js +0 -418
- package/src/CLI/symbol_indexer.js +0 -231
- package/src/CLI/updater.js +0 -230
- package/src/CLI/workspace_manager.js +0 -90
- package/src/Channels/brave_search_bridge.js +0 -35
- package/src/Channels/discord_bridge.js +0 -66
- package/src/Channels/google_search_bridge.js +0 -38
- package/src/Channels/line_bridge.js +0 -60
- package/src/Channels/slack_bridge.js +0 -48
- package/src/Channels/telegram_bridge.js +0 -41
- package/src/Channels/whatsapp_bridge.js +0 -57
- package/src/Command_Parser/parser.js +0 -45
- package/src/Plugins/dev_tools.js +0 -41
- package/src/Plugins/discord.js +0 -20
- package/src/Plugins/docker.js +0 -47
- package/src/Plugins/gmail.js +0 -251
- package/src/Plugins/google_calendar.js +0 -252
- package/src/Plugins/mcp_manager.js +0 -95
- package/src/Plugins/notion.js +0 -256
- package/src/Plugins/obsidian.js +0 -54
- package/src/Plugins/plugin_manager.js +0 -81
- package/src/Plugins/spotify.js +0 -173
- package/src/Plugins/system_metrics.js +0 -31
- package/src/Plugins/system_monitor.js +0 -72
- package/src/System/action_executor.js +0 -178
- package/src/System/bridge_manager.js +0 -76
- package/src/System/chat_history_manager.js +0 -83
- package/src/System/config_manager.js +0 -194
- package/src/System/custom_workflows.js +0 -163
- package/src/System/daemon_manager.js +0 -67
- package/src/System/google_tts_urls.js +0 -51
- package/src/System/granular_automation.js +0 -157
- package/src/System/ipc_handlers.js +0 -332
- package/src/System/notifications.js +0 -23
- package/src/System/optional_require.js +0 -23
- package/src/System/picture_store.js +0 -109
- package/src/System/proactive_loop.js +0 -153
- package/src/System/safety_manager.js +0 -273
- package/src/System/sandbox_runner.js +0 -182
- package/src/System/screen_capture.js +0 -175
- package/src/System/smart_context.js +0 -227
- package/src/System/system_automation.js +0 -162
- package/src/System/system_events.js +0 -79
- package/src/System/system_info.js +0 -125
- package/src/System/task_manager.js +0 -222
- package/src/System/tool_registry.js +0 -293
- package/src/System/window_manager.js +0 -220
- package/src/UI/floating.css +0 -80
- package/src/UI/floating.html +0 -17
- package/src/UI/floating.js +0 -67
- package/src/UI/live2d_manager.js +0 -600
- package/src/UI/preload-floating.js +0 -7
- package/src/UI/preload-spotlight.js +0 -11
- package/src/UI/preload-widget.js +0 -5
- package/src/UI/proactive-glow.html +0 -42
- package/src/UI/renderer.js +0 -2127
- package/src/UI/screenPicker.html +0 -214
- package/src/UI/screenPicker.js +0 -262
- package/src/UI/settings.html +0 -577
- package/src/UI/settings.js +0 -770
- package/src/UI/spotlight.html +0 -23
- package/src/UI/spotlight.js +0 -185
- package/src/UI/widget.html +0 -29
- package/src/UI/widget.js +0 -10
- /package/{models → src/renderer/public/models}/Shiroko_Model/Shiroko/Shiroko_Core/72d86db84cfa9730b894c241fd24c0db.png +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//345/233/264/350/243/231.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/apron.exp3.json} +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//347/214/253/345/222/252/346/273/244/351/225/234.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/catfilter.exp3.json} +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//347/202/271/344/270/200/344/270/213.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/click.exp3.json} +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/dazed.exp3.json} +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253/347/234/274/347/217/240/346/221/207/346/231/203.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/dazedeyes.exp3.json} +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//347/234/274/351/225/234.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/glasses.exp3.json} +0 -0
- /package/{models → src/renderer/public/models}/Shiroko_Model/Shiroko/Shiroko_Core/items_pinned_to_model.json +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/277/347/254/224.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/pen.exp3.json} +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/215/347/205/247.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/photo.exp3.json} +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_00.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_00.png} +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_01.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_01.png} +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_02.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_02.png} +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_03.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_03.png} +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.cdi3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.cdi3.json} +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.moc3" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.moc3} +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.physics3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.physics3.json} +0 -0
- /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.vtube.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.vtube.json} +0 -0
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const os = require('os');
|
|
4
|
-
const crypto = require('crypto');
|
|
5
|
-
const { GoogleGenAI } = require('@google/genai');
|
|
6
|
-
const pdf = require('pdf-parse');
|
|
7
|
-
const mammoth = require('mammoth');
|
|
8
|
-
const readXlsxFile = require('read-excel-file/node');
|
|
9
|
-
const { readConfig } = require('../System/config_manager');
|
|
10
|
-
|
|
11
|
-
// Handle electron dependency safely
|
|
12
|
-
let app;
|
|
13
|
-
try {
|
|
14
|
-
const electron = require('electron');
|
|
15
|
-
app = electron.app;
|
|
16
|
-
} catch (e) {
|
|
17
|
-
app = null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
let ai = null;
|
|
21
|
-
let activeApiKey = '';
|
|
22
|
-
let DatabaseSync = null;
|
|
23
|
-
|
|
24
|
-
function resolveApiKey() {
|
|
25
|
-
let settingsKey = '';
|
|
26
|
-
try {
|
|
27
|
-
const cfg = readConfig();
|
|
28
|
-
settingsKey = (cfg.apiKey || '').trim();
|
|
29
|
-
} catch (e) {
|
|
30
|
-
settingsKey = '';
|
|
31
|
-
}
|
|
32
|
-
const selectedKey = settingsKey || process.env.GEMINI_API_KEY || '';
|
|
33
|
-
activeApiKey = selectedKey;
|
|
34
|
-
return selectedKey;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function getAiClient() {
|
|
38
|
-
const key = resolveApiKey();
|
|
39
|
-
if (!ai || activeApiKey !== key) {
|
|
40
|
-
ai = new GoogleGenAI({ apiKey: key });
|
|
41
|
-
}
|
|
42
|
-
return ai;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function getDbPath() {
|
|
46
|
-
const fileName = 'mint-knowledge.sqlite';
|
|
47
|
-
const configDir = path.join(os.homedir(), '.config', 'mint');
|
|
48
|
-
const dbPath = path.join(configDir, fileName);
|
|
49
|
-
|
|
50
|
-
if (!fs.existsSync(configDir)) {
|
|
51
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Migration Logic
|
|
55
|
-
if (!fs.existsSync(dbPath)) {
|
|
56
|
-
const electronDb = app && app.getPath ? path.join(app.getPath('userData'), fileName) : null;
|
|
57
|
-
const legacyDb = path.join(os.homedir(), '.mint', fileName);
|
|
58
|
-
|
|
59
|
-
if (electronDb && fs.existsSync(electronDb)) {
|
|
60
|
-
try {
|
|
61
|
-
fs.copyFileSync(electronDb, dbPath);
|
|
62
|
-
console.log('[RAG] Migrated database from Electron userData');
|
|
63
|
-
} catch (e) { console.error('[RAG] Migration from Electron failed:', e); }
|
|
64
|
-
} else if (fs.existsSync(legacyDb)) {
|
|
65
|
-
try {
|
|
66
|
-
fs.copyFileSync(legacyDb, dbPath);
|
|
67
|
-
console.log('[RAG] Migrated database from ~/.mint');
|
|
68
|
-
} catch (e) { console.error('[RAG] Migration from ~/.mint failed:', e); }
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return dbPath;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function getDatabaseSync() {
|
|
76
|
-
if (!DatabaseSync) {
|
|
77
|
-
({ DatabaseSync } = require('node:sqlite'));
|
|
78
|
-
}
|
|
79
|
-
return DatabaseSync;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Initialize Database
|
|
83
|
-
let dbInstance = null;
|
|
84
|
-
function getDb() {
|
|
85
|
-
if (dbInstance) return dbInstance;
|
|
86
|
-
const dbPath = getDbPath();
|
|
87
|
-
const Database = getDatabaseSync();
|
|
88
|
-
dbInstance = new Database(dbPath);
|
|
89
|
-
|
|
90
|
-
// Enable WAL mode for better concurrency
|
|
91
|
-
dbInstance.exec('PRAGMA journal_mode = WAL;');
|
|
92
|
-
dbInstance.exec('PRAGMA synchronous = NORMAL;');
|
|
93
|
-
|
|
94
|
-
// Create Tables
|
|
95
|
-
dbInstance.exec(`
|
|
96
|
-
-- Shared knowledge tables
|
|
97
|
-
CREATE TABLE IF NOT EXISTS sources (
|
|
98
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
99
|
-
path TEXT UNIQUE,
|
|
100
|
-
name TEXT,
|
|
101
|
-
hash TEXT,
|
|
102
|
-
last_indexed DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
103
|
-
);
|
|
104
|
-
CREATE TABLE IF NOT EXISTS chunks (
|
|
105
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
106
|
-
source_id INTEGER,
|
|
107
|
-
text TEXT,
|
|
108
|
-
embedding BLOB,
|
|
109
|
-
FOREIGN KEY(source_id) REFERENCES sources(id) ON DELETE CASCADE
|
|
110
|
-
);
|
|
111
|
-
CREATE INDEX IF NOT EXISTS idx_chunks_source ON chunks(source_id);
|
|
112
|
-
|
|
113
|
-
-- Shared memory tables (ensuring consistency)
|
|
114
|
-
CREATE TABLE IF NOT EXISTS user_profile (
|
|
115
|
-
key TEXT PRIMARY KEY,
|
|
116
|
-
value TEXT,
|
|
117
|
-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
118
|
-
);
|
|
119
|
-
CREATE TABLE IF NOT EXISTS session_memories (
|
|
120
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
121
|
-
summary TEXT NOT NULL,
|
|
122
|
-
tags TEXT DEFAULT '',
|
|
123
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
124
|
-
);
|
|
125
|
-
CREATE TABLE IF NOT EXISTS usage_patterns (
|
|
126
|
-
pattern TEXT PRIMARY KEY,
|
|
127
|
-
count INTEGER DEFAULT 1,
|
|
128
|
-
last_used DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
129
|
-
);
|
|
130
|
-
CREATE TABLE IF NOT EXISTS response_cache (
|
|
131
|
-
query_hash TEXT PRIMARY KEY,
|
|
132
|
-
response TEXT NOT NULL,
|
|
133
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
134
|
-
);
|
|
135
|
-
`);
|
|
136
|
-
return dbInstance;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
async function generateEmbedding(text) {
|
|
140
|
-
const client = getAiClient();
|
|
141
|
-
const response = await client.models.embedContent({
|
|
142
|
-
model: 'gemini-embedding-001',
|
|
143
|
-
contents: text,
|
|
144
|
-
});
|
|
145
|
-
return response.embeddings[0].values;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
function cosineSimilarity(vecA, vecB) {
|
|
150
|
-
let dotProduct = 0, normA = 0, normB = 0;
|
|
151
|
-
for (let i = 0; i < vecA.length; i++) {
|
|
152
|
-
dotProduct += vecA[i] * vecB[i];
|
|
153
|
-
normA += vecA[i] * vecA[i];
|
|
154
|
-
normB += vecB[i] * vecB[i];
|
|
155
|
-
}
|
|
156
|
-
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function getFileHash(filePath) {
|
|
160
|
-
const content = fs.readFileSync(filePath);
|
|
161
|
-
return crypto.createHash('md5').update(content).digest('hex');
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function chunkText(text, maxChars = 1000, overlap = 200) {
|
|
165
|
-
const chunks = [];
|
|
166
|
-
let current = 0;
|
|
167
|
-
while (current < text.length) {
|
|
168
|
-
chunks.push(text.slice(current, current + maxChars));
|
|
169
|
-
current += (maxChars - overlap);
|
|
170
|
-
if (current >= text.length) break;
|
|
171
|
-
}
|
|
172
|
-
return chunks;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
async function indexFile(filePath) {
|
|
176
|
-
try {
|
|
177
|
-
if (!fs.existsSync(filePath)) return `ไม่พบไฟล์: ${filePath}`;
|
|
178
|
-
const stats = fs.statSync(filePath);
|
|
179
|
-
if (stats.isDirectory()) return await indexFolder(filePath);
|
|
180
|
-
if (stats.size > 10 * 1024 * 1024) return `ไฟล์ใหญ่เกินไป (> 10MB): ${filePath}`;
|
|
181
|
-
|
|
182
|
-
const hash = getFileHash(filePath);
|
|
183
|
-
const db = getDb();
|
|
184
|
-
|
|
185
|
-
// Check if already indexed and unchanged
|
|
186
|
-
const checkStmt = db.prepare("SELECT id, hash FROM sources WHERE path = ?");
|
|
187
|
-
const existing = checkStmt.get(filePath);
|
|
188
|
-
|
|
189
|
-
if (existing && existing.hash === hash) {
|
|
190
|
-
return `⏩ ${path.basename(filePath)} ไม่มีการเปลี่ยนแปลง (ข้ามการอ่าน)`;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
console.log(`[RAG] Indexing ${filePath}...`);
|
|
194
|
-
let content = '';
|
|
195
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
196
|
-
|
|
197
|
-
// Extraction logic
|
|
198
|
-
if (ext === '.pdf') {
|
|
199
|
-
const data = await pdf(fs.readFileSync(filePath));
|
|
200
|
-
content = data.text;
|
|
201
|
-
} else if (ext === '.docx') {
|
|
202
|
-
const res = await mammoth.extractRawText({ path: filePath });
|
|
203
|
-
content = res.value;
|
|
204
|
-
} else if (ext === '.xlsx') {
|
|
205
|
-
const sheets = await readXlsxFile(filePath);
|
|
206
|
-
content = sheets
|
|
207
|
-
.map(({ sheet, data }) => [
|
|
208
|
-
`Sheet: ${sheet}`,
|
|
209
|
-
...data.map(row => row.map(value => value == null ? '' : String(value)).join(','))
|
|
210
|
-
].join('\n'))
|
|
211
|
-
.join('\n');
|
|
212
|
-
} else {
|
|
213
|
-
content = fs.readFileSync(filePath, 'utf8');
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (!content.trim()) return `⚠️ ไฟล์ไม่มีข้อความ: ${filePath}`;
|
|
217
|
-
|
|
218
|
-
// Begin transaction
|
|
219
|
-
db.exec("BEGIN TRANSACTION");
|
|
220
|
-
try {
|
|
221
|
-
if (existing) {
|
|
222
|
-
db.prepare("DELETE FROM chunks WHERE source_id = ?").run(existing.id);
|
|
223
|
-
db.prepare("UPDATE sources SET hash = ?, last_indexed = CURRENT_TIMESTAMP WHERE id = ?").run(hash, existing.id);
|
|
224
|
-
} else {
|
|
225
|
-
db.prepare("INSERT INTO sources (path, name, hash) VALUES (?, ?, ?)").run(filePath, path.basename(filePath), hash);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const sourceId = existing ? existing.id : db.prepare("SELECT last_insert_rowid() as id").get().id;
|
|
229
|
-
const chunks = chunkText(content);
|
|
230
|
-
|
|
231
|
-
const insertChunk = db.prepare("INSERT INTO chunks (source_id, text, embedding) VALUES (?, ?, ?)");
|
|
232
|
-
for (const chunk of chunks) {
|
|
233
|
-
const embedding = await generateEmbedding(chunk);
|
|
234
|
-
const embeddingBlob = Buffer.from(new Float32Array(embedding).buffer);
|
|
235
|
-
insertChunk.run(sourceId, chunk, embeddingBlob);
|
|
236
|
-
}
|
|
237
|
-
db.exec("COMMIT");
|
|
238
|
-
return `✅ Successfully indexed ${path.basename(filePath)} (${chunks.length} chunks)`;
|
|
239
|
-
} catch (e) {
|
|
240
|
-
db.exec("ROLLBACK");
|
|
241
|
-
throw e;
|
|
242
|
-
}
|
|
243
|
-
} catch (err) {
|
|
244
|
-
console.error('[RAG] Error:', err);
|
|
245
|
-
return `❌ Failed to index: ${err.message}`;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Recursively gets all files in a directory asynchronously
|
|
251
|
-
*/
|
|
252
|
-
async function getAllFiles(dirPath, arrayOfFiles = []) {
|
|
253
|
-
const files = await fs.promises.readdir(dirPath, { withFileTypes: true });
|
|
254
|
-
|
|
255
|
-
for (const file of files) {
|
|
256
|
-
const fullPath = path.join(dirPath, file.name);
|
|
257
|
-
if (file.isDirectory()) {
|
|
258
|
-
await getAllFiles(fullPath, arrayOfFiles);
|
|
259
|
-
} else {
|
|
260
|
-
arrayOfFiles.push(fullPath);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return arrayOfFiles;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
async function indexFolder(folderPath) {
|
|
267
|
-
console.log(`[RAG] Indexing folder: ${folderPath}`);
|
|
268
|
-
const files = await getAllFiles(folderPath);
|
|
269
|
-
console.log(`[RAG] Found ${files.length} files to check.`);
|
|
270
|
-
|
|
271
|
-
// Process in small batches to avoid blocking
|
|
272
|
-
const BATCH_SIZE = 5;
|
|
273
|
-
let indexedCount = 0;
|
|
274
|
-
let skippedCount = 0;
|
|
275
|
-
|
|
276
|
-
for (let i = 0; i < files.length; i += BATCH_SIZE) {
|
|
277
|
-
const batch = files.slice(i, i + BATCH_SIZE);
|
|
278
|
-
await Promise.all(batch.map(async (file) => {
|
|
279
|
-
const res = await indexFile(file);
|
|
280
|
-
if (res && res.startsWith('✅')) indexedCount++;
|
|
281
|
-
else skippedCount++;
|
|
282
|
-
}));
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
console.log(`[RAG] Indexing complete. ${indexedCount} new/updated, ${skippedCount} skipped.`);
|
|
286
|
-
return `📂 Folder indexing complete: ${indexedCount} learned, ${skippedCount} skipped.`;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
async function searchKnowledge(query, topK = 3) {
|
|
290
|
-
const startTime = Date.now();
|
|
291
|
-
const db = getDb();
|
|
292
|
-
const MAX_CHUNKS_TO_SEARCH = 2000; // Limit search to keep it fast
|
|
293
|
-
|
|
294
|
-
const countRes = db.prepare("SELECT COUNT(*) as count FROM chunks").get();
|
|
295
|
-
if (!countRes || countRes.count === 0) return null;
|
|
296
|
-
|
|
297
|
-
try {
|
|
298
|
-
const queryVector = await generateEmbedding(query);
|
|
299
|
-
const queryTyped = new Float32Array(queryVector);
|
|
300
|
-
const results = [];
|
|
301
|
-
|
|
302
|
-
// Search most recent or top chunks first, but limit the total scan
|
|
303
|
-
const stmt = db.prepare("SELECT text, embedding, source_id FROM chunks LIMIT ?");
|
|
304
|
-
let processed = 0;
|
|
305
|
-
|
|
306
|
-
for (const c of stmt.iterate(MAX_CHUNKS_TO_SEARCH)) {
|
|
307
|
-
if (!c.embedding) continue;
|
|
308
|
-
processed++;
|
|
309
|
-
|
|
310
|
-
const chunkVector = new Float32Array(c.embedding.buffer, c.embedding.byteOffset, c.embedding.byteLength / 4);
|
|
311
|
-
|
|
312
|
-
let dotProduct = 0, normA = 0, normB = 0;
|
|
313
|
-
for (let i = 0; i < queryTyped.length; i++) {
|
|
314
|
-
const a = queryTyped[i];
|
|
315
|
-
const b = chunkVector[i];
|
|
316
|
-
dotProduct += a * b;
|
|
317
|
-
normA += a * a;
|
|
318
|
-
normB += b * b;
|
|
319
|
-
}
|
|
320
|
-
const score = dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
321
|
-
|
|
322
|
-
if (score > 0.65) {
|
|
323
|
-
results.push({ text: c.text, score, source_id: c.source_id });
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
if (results.length > 0) {
|
|
328
|
-
results.sort((a, b) => b.score - a.score);
|
|
329
|
-
const top = results.slice(0, topK);
|
|
330
|
-
|
|
331
|
-
const sourceIds = [...new Set(top.map(t => t.source_id))];
|
|
332
|
-
const sources = db.prepare(`SELECT id, name FROM sources WHERE id IN (${sourceIds.join(',')})`).all();
|
|
333
|
-
const sourceMap = Object.fromEntries(sources.map(s => [s.id, s.name]));
|
|
334
|
-
|
|
335
|
-
console.log(`[RAG] Search took ${Date.now() - startTime}ms for ${processed} chunks.`);
|
|
336
|
-
return top.map(t => ({
|
|
337
|
-
text: t.text,
|
|
338
|
-
source: sourceMap[t.source_id],
|
|
339
|
-
score: t.score
|
|
340
|
-
}));
|
|
341
|
-
}
|
|
342
|
-
} catch (e) {
|
|
343
|
-
console.error("[RAG] Search Error:", e);
|
|
344
|
-
}
|
|
345
|
-
return null;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
module.exports = { indexFile, indexFolder, searchKnowledge };
|