simple-dynamsoft-mcp 7.1.1 → 7.2.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/.env.example +29 -97
- package/README.md +173 -121
- package/package.json +1 -3
- package/scripts/prebuild-rag-index.mjs +5 -5
- package/scripts/run-gemini-tests.mjs +0 -3
- package/src/data/hydration-mode.js +15 -1
- package/src/rag/config.js +2 -6
- package/src/rag/index.js +34 -0
- package/src/rag/logger.js +3 -2
- package/src/rag/profile-config.js +34 -32
- package/src/rag/providers.js +1 -45
- package/src/rag/vector-cache.js +2 -4
package/.env.example
CHANGED
|
@@ -1,115 +1,47 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
# - semantic-local: local embeddings via @xenova/transformers
|
|
5
|
-
# - semantic-gemini: remote embeddings via Google Gemini
|
|
6
|
-
MCP_PROFILE=lite
|
|
1
|
+
# Minimal configuration
|
|
2
|
+
# - If GEMINI_API_KEY is set: provider=gemini, fallback=lexical, hydration_mode=eager
|
|
3
|
+
# - If GEMINI_API_KEY is unset: provider=lexical, fallback=none, hydration_mode=lazy
|
|
7
4
|
|
|
8
|
-
#
|
|
9
|
-
# RAG_PROVIDER: lexical | gemini | local | fuse
|
|
10
|
-
# - lexical: hybrid lexical retrieval (BM25 + Fuse)
|
|
11
|
-
# - gemini: remote embeddings via Google Gemini
|
|
12
|
-
# - local: local embeddings via @xenova/transformers
|
|
13
|
-
# - fuse: compatibility fallback (fuzzy-only, no embeddings)
|
|
14
|
-
RAG_PROVIDER=lexical
|
|
5
|
+
# GEMINI_API_KEY=your_key_here
|
|
15
6
|
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
RAG_FALLBACK=none
|
|
22
|
-
|
|
23
|
-
# Gemini remote embeddings (required when RAG_PROVIDER=gemini)
|
|
24
|
-
# * GEMINI_API_KEY: your Gemini API key (never commit to git)
|
|
25
|
-
# * GEMINI_EMBED_MODEL: models/embedding-001 | models/gemini-embedding-001 | gemini-embedding-001
|
|
26
|
-
# * GEMINI_API_BASE_URL: override for proxies (default https://generativelanguage.googleapis.com)
|
|
27
|
-
# * GEMINI_EMBED_BATCH_SIZE: batch size for Gemini embedding requests
|
|
28
|
-
# * GEMINI_RETRY_MAX_ATTEMPTS: max retry attempts for retryable Gemini errors
|
|
29
|
-
# * GEMINI_RETRY_BASE_DELAY_MS: base delay for exponential backoff
|
|
30
|
-
# * GEMINI_RETRY_MAX_DELAY_MS: max delay cap for exponential backoff
|
|
31
|
-
# * GEMINI_REQUEST_THROTTLE_MS: fixed delay between Gemini requests
|
|
32
|
-
# GEMINI_API_KEY=your_key
|
|
33
|
-
# GEMINI_EMBED_MODEL=gemini-embedding-001
|
|
34
|
-
# GEMINI_API_BASE_URL=https://generativelanguage.googleapis.com
|
|
35
|
-
# GEMINI_EMBED_BATCH_SIZE=16
|
|
36
|
-
# GEMINI_RETRY_MAX_ATTEMPTS=5
|
|
37
|
-
# GEMINI_RETRY_BASE_DELAY_MS=500
|
|
38
|
-
# GEMINI_RETRY_MAX_DELAY_MS=10000
|
|
39
|
-
# GEMINI_REQUEST_THROTTLE_MS=0
|
|
40
|
-
|
|
41
|
-
# Local embeddings (used when RAG_PROVIDER=local or fallback=local)
|
|
42
|
-
# * RAG_LOCAL_MODEL: Hugging Face model id (default Xenova/all-MiniLM-L6-v2)
|
|
43
|
-
# * RAG_LOCAL_QUANTIZED: true|false (smaller/faster model download when true)
|
|
44
|
-
# RAG_LOCAL_MODEL=Xenova/all-MiniLM-L6-v2
|
|
45
|
-
# RAG_LOCAL_QUANTIZED=true
|
|
7
|
+
# Optional: explicit cache directories
|
|
8
|
+
# MCP_DATA_DIR=
|
|
9
|
+
# MCP_DATA_CACHE_DIR=
|
|
10
|
+
# RAG_CACHE_DIR=
|
|
11
|
+
# RAG_MODEL_CACHE_DIR=
|
|
46
12
|
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
# RAG_CACHE_DIR=data/.rag-cache
|
|
51
|
-
# RAG_MODEL_CACHE_DIR=data/.rag-cache/models
|
|
13
|
+
# Optional: startup behavior
|
|
14
|
+
# MCP_DATA_AUTO_DOWNLOAD=true
|
|
15
|
+
# MCP_DATA_REFRESH_ON_START=false
|
|
52
16
|
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
# * RAG_CHUNK_OVERLAP: overlap between chunks (helps context continuity)
|
|
56
|
-
# * RAG_MAX_CHUNKS_PER_DOC: cap chunks per doc to control index size
|
|
57
|
-
# * RAG_MAX_TEXT_CHARS: max chars per embedding input
|
|
58
|
-
# * RAG_MIN_SCORE: minimum cosine similarity to keep a hit (0 disables filtering). Default 0.2.
|
|
59
|
-
# * RAG_INCLUDE_SCORE: include similarity score in search results (debugging)
|
|
60
|
-
# RAG_CHUNK_SIZE=1200
|
|
61
|
-
# RAG_CHUNK_OVERLAP=200
|
|
62
|
-
# RAG_MAX_CHUNKS_PER_DOC=6
|
|
63
|
-
# RAG_MAX_TEXT_CHARS=4000
|
|
64
|
-
# RAG_MIN_SCORE=0.2
|
|
65
|
-
# RAG_INCLUDE_SCORE=false
|
|
17
|
+
# Optional: force hydration mode override
|
|
18
|
+
# MCP_DATA_HYDRATION_MODE=eager
|
|
66
19
|
|
|
67
|
-
#
|
|
68
|
-
# * RAG_REBUILD: true to ignore cache and rebuild on startup/search
|
|
69
|
-
# * RAG_PREWARM: true to build the embedding index at startup
|
|
70
|
-
# * RAG_PREWARM_BLOCK: true to block startup until prewarm completes
|
|
71
|
-
# * RAG_PREBUILT_INDEX_AUTO_DOWNLOAD: auto-download prebuilt index when local or gemini embeddings are selected
|
|
72
|
-
# * RAG_PREBUILT_INDEX_URL: global override URL for prebuilt index archive (applies to both local and gemini providers)
|
|
73
|
-
# * RAG_PREBUILT_INDEX_URL_LOCAL: provider-specific URL override for local prebuilt index archive
|
|
74
|
-
# * RAG_PREBUILT_INDEX_URL_GEMINI: provider-specific URL override for gemini prebuilt index archive
|
|
75
|
-
# * RAG_PREBUILT_INDEX_TIMEOUT_MS: timeout for prebuilt index download request
|
|
76
|
-
# RAG_REBUILD=false
|
|
77
|
-
# RAG_PREWARM=false
|
|
78
|
-
# RAG_PREWARM_BLOCK=false
|
|
20
|
+
# Optional: prebuilt Gemini index behavior
|
|
79
21
|
# RAG_PREBUILT_INDEX_AUTO_DOWNLOAD=true
|
|
80
22
|
# RAG_PREBUILT_INDEX_URL=
|
|
81
|
-
# RAG_PREBUILT_INDEX_URL_LOCAL=
|
|
82
23
|
# RAG_PREBUILT_INDEX_URL_GEMINI=
|
|
83
24
|
# RAG_PREBUILT_INDEX_TIMEOUT_MS=180000
|
|
84
25
|
|
|
85
|
-
# Optional
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
# DATA_SYNC_ON_START=false
|
|
89
|
-
# DATA_SYNC_TIMEOUT_MS=30000
|
|
26
|
+
# Optional: prewarm behavior
|
|
27
|
+
# RAG_PREWARM=true
|
|
28
|
+
# RAG_PREWARM_BLOCK=false
|
|
90
29
|
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
|
|
100
|
-
#
|
|
101
|
-
# MCP_DATA_DIR=
|
|
102
|
-
# MCP_DATA_AUTO_DOWNLOAD=true
|
|
103
|
-
# MCP_DATA_CACHE_DIR=
|
|
104
|
-
# MCP_DATA_REFRESH_ON_START=false
|
|
105
|
-
# MCP_DATA_HYDRATION_MODE=lazy
|
|
30
|
+
# Optional: Gemini request tuning
|
|
31
|
+
# GEMINI_EMBED_MODEL=gemini-embedding-001
|
|
32
|
+
# GEMINI_API_BASE_URL=https://generativelanguage.googleapis.com
|
|
33
|
+
# GEMINI_EMBED_BATCH_SIZE=16
|
|
34
|
+
# GEMINI_RETRY_MAX_ATTEMPTS=5
|
|
35
|
+
# GEMINI_RETRY_BASE_DELAY_MS=500
|
|
36
|
+
# GEMINI_RETRY_MAX_DELAY_MS=10000
|
|
37
|
+
# GEMINI_REQUEST_THROTTLE_MS=0
|
|
38
|
+
|
|
39
|
+
# Optional: data download retry tuning
|
|
106
40
|
# MCP_DATA_DOWNLOAD_TIMEOUT_MS=180000
|
|
107
41
|
# MCP_DATA_DOWNLOAD_RETRY_MAX_ATTEMPTS=3
|
|
108
42
|
# MCP_DATA_DOWNLOAD_RETRY_BASE_DELAY_MS=500
|
|
109
43
|
# MCP_DATA_DOWNLOAD_RETRY_MAX_DELAY_MS=5000
|
|
110
44
|
|
|
111
|
-
#
|
|
112
|
-
# * MCP_VERBOSE_LOGS: true to include debug-level diagnostics
|
|
113
|
-
# * MCP_LOG_LEVEL: info | debug (debug enables verbose logs)
|
|
45
|
+
# Optional: observability
|
|
114
46
|
# MCP_VERBOSE_LOGS=false
|
|
115
47
|
# MCP_LOG_LEVEL=info
|
package/README.md
CHANGED
|
@@ -14,146 +14,167 @@ Default transport is `stdio`. Native Streamable HTTP is also supported at `/mcp`
|
|
|
14
14
|
|
|
15
15
|
https://github.com/user-attachments/assets/cc1c5f4b-1461-4462-897a-75abc20d62a6
|
|
16
16
|
|
|
17
|
-
## Two
|
|
17
|
+
## Two Core Usage Modes
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
1. Remote MCP over HTTP (recommended)
|
|
20
|
+
2. Local MCP via `npx`
|
|
20
21
|
|
|
21
|
-
1
|
|
22
|
-
2. HTTP deployment on Ubuntu with full data + prebuilt indexes + Gemini embeddings
|
|
22
|
+
### 1) Remote (Recommended)
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Use this endpoint directly:
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
- `https://simple-dynamsoft-mcp.wonderfulwave-69908b91.eastus2.azurecontainerapps.io/mcp`
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Command:
|
|
28
|
+
### 2) Local
|
|
31
29
|
|
|
32
30
|
```bash
|
|
33
31
|
npx -y simple-dynamsoft-mcp@latest
|
|
34
32
|
```
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
- No explicit environment variables are required for the default path.
|
|
38
|
-
- Default profile is lightweight (`lite`) and avoids local embedding model downloads.
|
|
39
|
-
- If local data is missing, the package can bootstrap pinned data from cache/download sources.
|
|
34
|
+
## Deployment Guides
|
|
40
35
|
|
|
41
|
-
|
|
36
|
+
- Azure Container Apps runbook: `docs/deployment/azure-container-apps.md`
|
|
37
|
+
- Self-hosting (Ubuntu/any server): `docs/deployment/self-hosting.md`
|
|
42
38
|
|
|
43
|
-
|
|
39
|
+
## MCP Client Configuration
|
|
44
40
|
|
|
45
|
-
|
|
41
|
+
Use one of the following client configs. Remote is recommended.
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
sudo apt update
|
|
49
|
-
sudo apt install -y git curl
|
|
50
|
-
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
|
51
|
-
sudo apt install -y nodejs
|
|
52
|
-
node -v
|
|
53
|
-
npm -v
|
|
54
|
-
```
|
|
43
|
+
### OpenCode
|
|
55
44
|
|
|
56
|
-
|
|
45
|
+
<details>
|
|
46
|
+
<summary>OpenCode Config</summary>
|
|
57
47
|
|
|
58
|
-
|
|
59
|
-
git clone --recurse-submodules https://github.com/yushulx/simple-dynamsoft-mcp.git
|
|
60
|
-
cd simple-dynamsoft-mcp
|
|
61
|
-
npm ci
|
|
62
|
-
```
|
|
48
|
+
Remote (recommended):
|
|
63
49
|
|
|
64
|
-
|
|
50
|
+
Location:
|
|
51
|
+
- macOS: `~/.config/opencode/opencode.json`
|
|
52
|
+
- Windows: `%USERPROFILE%\.config\opencode\opencode.json`
|
|
65
53
|
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"$schema": "https://opencode.ai/config.json",
|
|
57
|
+
"mcp": {
|
|
58
|
+
"dynamsoft": {
|
|
59
|
+
"type": "remote",
|
|
60
|
+
"url": "https://simple-dynamsoft-mcp.wonderfulwave-69908b91.eastus2.azurecontainerapps.io/mcp"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
69
64
|
```
|
|
70
65
|
|
|
71
|
-
|
|
66
|
+
Local:
|
|
72
67
|
|
|
73
|
-
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"$schema": "https://opencode.ai/config.json",
|
|
71
|
+
"mcp": {
|
|
72
|
+
"dynamsoft": {
|
|
73
|
+
"type": "local",
|
|
74
|
+
"command": ["npx", "-y", "simple-dynamsoft-mcp@latest"]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
74
79
|
|
|
75
|
-
|
|
76
|
-
GEMINI_API_KEY=your_key_here
|
|
80
|
+
</details>
|
|
77
81
|
|
|
78
|
-
|
|
79
|
-
RAG_PROVIDER=gemini
|
|
80
|
-
RAG_FALLBACK=lexical
|
|
82
|
+
### Claude Desktop
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
MCP_DATA_REFRESH_ON_START=false
|
|
84
|
+
<details>
|
|
85
|
+
<summary>Claude Desktop Config</summary>
|
|
85
86
|
|
|
86
|
-
|
|
87
|
+
Location:
|
|
88
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
89
|
+
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
90
|
+
|
|
91
|
+
Remote (recommended):
|
|
87
92
|
|
|
88
|
-
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"mcpServers": {
|
|
96
|
+
"dynamsoft": {
|
|
97
|
+
"url": "https://simple-dynamsoft-mcp.wonderfulwave-69908b91.eastus2.azurecontainerapps.io/mcp"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
89
101
|
```
|
|
90
102
|
|
|
91
|
-
|
|
103
|
+
Local:
|
|
92
104
|
|
|
93
|
-
```
|
|
94
|
-
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"mcpServers": {
|
|
108
|
+
"dynamsoft": {
|
|
109
|
+
"command": "npx",
|
|
110
|
+
"args": ["-y", "simple-dynamsoft-mcp@latest"]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
95
114
|
```
|
|
96
115
|
|
|
97
|
-
|
|
98
|
-
- `http://<server-ip>:3333/mcp`
|
|
116
|
+
</details>
|
|
99
117
|
|
|
100
|
-
###
|
|
118
|
+
### VS Code with GitHub Copilot
|
|
101
119
|
|
|
102
|
-
|
|
120
|
+
<details>
|
|
121
|
+
<summary>VS Code MCP Config</summary>
|
|
103
122
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
After=network.target
|
|
123
|
+
Global location:
|
|
124
|
+
- macOS: `~/Library/Application Support/Code/User/mcp.json`
|
|
125
|
+
- Windows: `%APPDATA%\Code\User\mcp.json`
|
|
108
126
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
ExecStart=/usr/bin/node /opt/simple-dynamsoft-mcp/src/index.js --transport=http --host=0.0.0.0 --port=3333
|
|
113
|
-
EnvironmentFile=/opt/simple-dynamsoft-mcp/.env
|
|
114
|
-
Restart=always
|
|
115
|
-
RestartSec=3
|
|
127
|
+
Workspace alternative: `.vscode/mcp.json`
|
|
128
|
+
|
|
129
|
+
Remote (recommended):
|
|
116
130
|
|
|
117
|
-
|
|
118
|
-
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"servers": {
|
|
134
|
+
"dynamsoft": {
|
|
135
|
+
"url": "https://simple-dynamsoft-mcp.wonderfulwave-69908b91.eastus2.azurecontainerapps.io/mcp"
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
119
139
|
```
|
|
120
140
|
|
|
121
|
-
|
|
141
|
+
Local:
|
|
122
142
|
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"servers": {
|
|
146
|
+
"dynamsoft": {
|
|
147
|
+
"command": "npx",
|
|
148
|
+
"args": ["-y", "simple-dynamsoft-mcp@latest"]
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
128
152
|
```
|
|
129
153
|
|
|
130
|
-
|
|
154
|
+
</details>
|
|
131
155
|
|
|
132
|
-
|
|
156
|
+
### Cursor
|
|
133
157
|
|
|
134
|
-
|
|
158
|
+
<details>
|
|
159
|
+
<summary>Cursor MCP Config</summary>
|
|
135
160
|
|
|
136
161
|
Location:
|
|
137
|
-
- macOS: `~/.
|
|
138
|
-
- Windows: `%USERPROFILE%\.
|
|
162
|
+
- macOS: `~/.cursor/mcp.json`
|
|
163
|
+
- Windows: `%USERPROFILE%\.cursor\mcp.json`
|
|
164
|
+
|
|
165
|
+
Remote (recommended):
|
|
139
166
|
|
|
140
167
|
```json
|
|
141
168
|
{
|
|
142
|
-
"
|
|
143
|
-
"mcp": {
|
|
169
|
+
"mcpServers": {
|
|
144
170
|
"dynamsoft": {
|
|
145
|
-
"
|
|
146
|
-
"command": ["npx", "-y", "simple-dynamsoft-mcp@latest"]
|
|
171
|
+
"url": "https://simple-dynamsoft-mcp.wonderfulwave-69908b91.eastus2.azurecontainerapps.io/mcp"
|
|
147
172
|
}
|
|
148
173
|
}
|
|
149
174
|
}
|
|
150
175
|
```
|
|
151
176
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
Location:
|
|
155
|
-
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
156
|
-
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
177
|
+
Local:
|
|
157
178
|
|
|
158
179
|
```json
|
|
159
180
|
{
|
|
@@ -166,15 +187,34 @@ Location:
|
|
|
166
187
|
}
|
|
167
188
|
```
|
|
168
189
|
|
|
169
|
-
|
|
190
|
+
</details>
|
|
170
191
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
192
|
+
### Windsurf
|
|
193
|
+
|
|
194
|
+
<details>
|
|
195
|
+
<summary>Windsurf MCP Config</summary>
|
|
196
|
+
|
|
197
|
+
Location:
|
|
198
|
+
- macOS: `~/.codeium/windsurf/mcp_config.json`
|
|
199
|
+
- Windows: `%USERPROFILE%\.codeium\windsurf\mcp_config.json`
|
|
200
|
+
|
|
201
|
+
Remote (recommended):
|
|
174
202
|
|
|
175
203
|
```json
|
|
176
204
|
{
|
|
177
|
-
"
|
|
205
|
+
"mcpServers": {
|
|
206
|
+
"dynamsoft": {
|
|
207
|
+
"url": "https://simple-dynamsoft-mcp.wonderfulwave-69908b91.eastus2.azurecontainerapps.io/mcp"
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Local:
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"mcpServers": {
|
|
178
218
|
"dynamsoft": {
|
|
179
219
|
"command": "npx",
|
|
180
220
|
"args": ["-y", "simple-dynamsoft-mcp@latest"]
|
|
@@ -183,30 +223,29 @@ Global location:
|
|
|
183
223
|
}
|
|
184
224
|
```
|
|
185
225
|
|
|
186
|
-
|
|
226
|
+
</details>
|
|
187
227
|
|
|
188
|
-
###
|
|
228
|
+
### Cline
|
|
229
|
+
|
|
230
|
+
<details>
|
|
231
|
+
<summary>Cline MCP Config</summary>
|
|
189
232
|
|
|
190
233
|
Location:
|
|
191
|
-
-
|
|
192
|
-
|
|
234
|
+
- VS Code settings JSON for Cline MCP integration
|
|
235
|
+
|
|
236
|
+
Remote (recommended):
|
|
193
237
|
|
|
194
238
|
```json
|
|
195
239
|
{
|
|
196
240
|
"mcpServers": {
|
|
197
241
|
"dynamsoft": {
|
|
198
|
-
"
|
|
199
|
-
"args": ["-y", "simple-dynamsoft-mcp@latest"]
|
|
242
|
+
"url": "https://simple-dynamsoft-mcp.wonderfulwave-69908b91.eastus2.azurecontainerapps.io/mcp"
|
|
200
243
|
}
|
|
201
244
|
}
|
|
202
245
|
}
|
|
203
246
|
```
|
|
204
247
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
Location:
|
|
208
|
-
- macOS: `~/.codeium/windsurf/mcp_config.json`
|
|
209
|
-
- Windows: `%USERPROFILE%\.codeium\windsurf\mcp_config.json`
|
|
248
|
+
Local:
|
|
210
249
|
|
|
211
250
|
```json
|
|
212
251
|
{
|
|
@@ -219,6 +258,38 @@ Location:
|
|
|
219
258
|
}
|
|
220
259
|
```
|
|
221
260
|
|
|
261
|
+
</details>
|
|
262
|
+
|
|
263
|
+
### Continue
|
|
264
|
+
|
|
265
|
+
<details>
|
|
266
|
+
<summary>Continue MCP Config</summary>
|
|
267
|
+
|
|
268
|
+
Location:
|
|
269
|
+
- `~/.continue/config.yaml` (or workspace Continue config)
|
|
270
|
+
|
|
271
|
+
Remote (recommended):
|
|
272
|
+
|
|
273
|
+
```yaml
|
|
274
|
+
mcpServers:
|
|
275
|
+
dynamsoft:
|
|
276
|
+
transport: streamable-http
|
|
277
|
+
url: https://simple-dynamsoft-mcp.wonderfulwave-69908b91.eastus2.azurecontainerapps.io/mcp
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Local:
|
|
281
|
+
|
|
282
|
+
```yaml
|
|
283
|
+
mcpServers:
|
|
284
|
+
dynamsoft:
|
|
285
|
+
command: npx
|
|
286
|
+
args:
|
|
287
|
+
- -y
|
|
288
|
+
- simple-dynamsoft-mcp@latest
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
</details>
|
|
292
|
+
|
|
222
293
|
## Available Tools
|
|
223
294
|
|
|
224
295
|
The server exposes this minimal tool surface:
|
|
@@ -230,25 +301,6 @@ The server exposes this minimal tool surface:
|
|
|
230
301
|
- `get_quickstart` -- opinionated quickstart: picks a sample by scenario, returns code + install instructions
|
|
231
302
|
- `get_sample_files` -- get full project files for a known sample (discovered via list_samples or search)
|
|
232
303
|
|
|
233
|
-
## Companion: Dynamsoft SDK Skills
|
|
234
|
-
|
|
235
|
-
For AI agents that support skills (Claude Code, OpenCode, Codex), install [dynamsoft-sdk-skills](https://github.com/user/dynamsoft-sdk-skills) for guided integration workflows:
|
|
236
|
-
|
|
237
|
-
npx dynamsoft-sdk-skills install --all
|
|
238
|
-
|
|
239
|
-
- **Skills** provide integration patterns, gotchas, and decision trees (loaded into agent context)
|
|
240
|
-
- **MCP Server** provides runtime tools: version resolution, doc search, sample browsing, and retrieval of full sample project files
|
|
241
|
-
|
|
242
|
-
Both work independently, but together the skills guide agents to use MCP tools at the right moments.
|
|
243
|
-
|
|
244
|
-
## Quick Troubleshooting
|
|
245
|
-
|
|
246
|
-
- If startup says data is incomplete, run `npm run data:bootstrap` and `npm run data:sync` in clone-based deployments.
|
|
247
|
-
- For HTTP deployments, check service logs first:
|
|
248
|
-
- `journalctl -u simple-dynamsoft-mcp -f`
|
|
249
|
-
- For Gemini mode, confirm `GEMINI_API_KEY` is present in service environment.
|
|
250
|
-
- Structured startup logs include `[data]`, `[transport]`, and `[rag]` event lines.
|
|
251
|
-
|
|
252
304
|
## Advanced Configuration And Operator Docs
|
|
253
305
|
|
|
254
306
|
Advanced settings, CI/runbook details, and maintenance workflows live in:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "simple-dynamsoft-mcp",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.2.1",
|
|
4
4
|
"description": "MCP server for Dynamsoft SDKs - Capture Vision, Barcode Reader (Mobile/Python/Web), Dynamic Web TWAIN, and Document Viewer. Provides documentation, code snippets, and API guidance.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -26,8 +26,6 @@
|
|
|
26
26
|
"test": "npm run test:lite",
|
|
27
27
|
"test:unit": "node --test test/unit/gemini-retry.test.js test/unit/profile-config.test.js test/unit/lexical-provider.test.js test/unit/hydration-mode.test.js test/unit/hydration-policy.test.js test/unit/repo-map.test.js test/unit/download-utils.test.js test/unit/logging.test.js test/unit/create-server.test.js test/unit/server-helpers.test.js",
|
|
28
28
|
"test:lite": "npm run test:stdio && npm run test:http && npm run test:package",
|
|
29
|
-
"test:fuse": "npm run test:lite",
|
|
30
|
-
"test:local": "node --test test/integration/stdio.test.js test/integration/http.test.js",
|
|
31
29
|
"test:lexical": "node --test test/integration/stdio.test.js test/integration/http.test.js",
|
|
32
30
|
"test:gemini": "node scripts/run-gemini-tests.mjs",
|
|
33
31
|
"test:stdio": "node --test test/integration/stdio.test.js",
|
|
@@ -13,7 +13,7 @@ function fileHash(path) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function ensureEnvDefaults() {
|
|
16
|
-
process.env.RAG_PROVIDER = process.env.RAG_PROVIDER || "
|
|
16
|
+
process.env.RAG_PROVIDER = process.env.RAG_PROVIDER || "gemini";
|
|
17
17
|
process.env.RAG_FALLBACK = process.env.RAG_FALLBACK || "none";
|
|
18
18
|
process.env.RAG_PREWARM = process.env.RAG_PREWARM || "true";
|
|
19
19
|
process.env.RAG_PREWARM_BLOCK = process.env.RAG_PREWARM_BLOCK || "true";
|
|
@@ -50,10 +50,10 @@ if (cacheFiles.length === 0) {
|
|
|
50
50
|
|
|
51
51
|
let indexSignature = "";
|
|
52
52
|
let indexCacheKey = "";
|
|
53
|
-
const
|
|
54
|
-
if (
|
|
53
|
+
const geminiCacheFile = cacheFiles.find((path) => /rag-gemini-.*\.json$/i.test(path));
|
|
54
|
+
if (geminiCacheFile) {
|
|
55
55
|
try {
|
|
56
|
-
const parsed = JSON.parse(readFileSync(
|
|
56
|
+
const parsed = JSON.parse(readFileSync(geminiCacheFile, "utf8"));
|
|
57
57
|
indexSignature = String(parsed?.meta?.signature || "");
|
|
58
58
|
indexCacheKey = String(parsed?.cacheKey || "");
|
|
59
59
|
} catch {
|
|
@@ -65,7 +65,7 @@ const manifest = {
|
|
|
65
65
|
packageVersion: pkg.version,
|
|
66
66
|
generatedAt: new Date().toISOString(),
|
|
67
67
|
ragProvider: ragConfig.provider,
|
|
68
|
-
ragModel: ragConfig.
|
|
68
|
+
ragModel: ragConfig.geminiModel,
|
|
69
69
|
indexSignature,
|
|
70
70
|
indexCacheKey,
|
|
71
71
|
cacheDir: toPosixPath(cacheDir),
|
|
@@ -8,8 +8,6 @@ const child = spawn(
|
|
|
8
8
|
stdio: "inherit",
|
|
9
9
|
env: {
|
|
10
10
|
...process.env,
|
|
11
|
-
RUN_FUSE_PROVIDER_TESTS: "false",
|
|
12
|
-
RUN_LOCAL_PROVIDER_TESTS: "false",
|
|
13
11
|
RUN_GEMINI_PROVIDER_TESTS: "true"
|
|
14
12
|
}
|
|
15
13
|
}
|
|
@@ -22,4 +20,3 @@ child.on("exit", (code, signal) => {
|
|
|
22
20
|
}
|
|
23
21
|
process.exit(code ?? 1);
|
|
24
22
|
});
|
|
25
|
-
|
|
@@ -3,9 +3,23 @@ function normalizeEnvValue(value) {
|
|
|
3
3
|
return String(value).trim().toLowerCase();
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
function resolveEffectiveProvider(env = process.env) {
|
|
7
|
+
const explicitProvider = normalizeEnvValue(env.RAG_PROVIDER);
|
|
8
|
+
const hasGeminiKey = normalizeEnvValue(env.GEMINI_API_KEY) !== "";
|
|
9
|
+
if (explicitProvider === "gemini" || explicitProvider === "lexical") {
|
|
10
|
+
return explicitProvider;
|
|
11
|
+
}
|
|
12
|
+
if (explicitProvider === "auto") {
|
|
13
|
+
return hasGeminiKey ? "gemini" : "lexical";
|
|
14
|
+
}
|
|
15
|
+
return hasGeminiKey ? "gemini" : "lexical";
|
|
16
|
+
}
|
|
17
|
+
|
|
6
18
|
function resolveHydrationMode(env = process.env) {
|
|
7
19
|
const mode = normalizeEnvValue(env.MCP_DATA_HYDRATION_MODE);
|
|
8
|
-
if (!mode)
|
|
20
|
+
if (!mode) {
|
|
21
|
+
return resolveEffectiveProvider(env) === "gemini" ? "eager" : "lazy";
|
|
22
|
+
}
|
|
9
23
|
if (mode === "lazy" || mode === "eager") return mode;
|
|
10
24
|
return "eager";
|
|
11
25
|
}
|
package/src/rag/config.js
CHANGED
|
@@ -13,8 +13,6 @@ const legacyPrebuiltIndexUrl =
|
|
|
13
13
|
`https://github.com/yushulx/simple-dynamsoft-mcp/releases/download/v${pkg.version}/prebuilt-rag-index-${pkg.version}.tar.gz`;
|
|
14
14
|
|
|
15
15
|
const defaultPrebuiltIndexUrls = {
|
|
16
|
-
local:
|
|
17
|
-
`https://github.com/yushulx/simple-dynamsoft-mcp/releases/download/v${pkg.version}/prebuilt-rag-index-local-${pkg.version}.tar.gz`,
|
|
18
16
|
gemini:
|
|
19
17
|
`https://github.com/yushulx/simple-dynamsoft-mcp/releases/download/v${pkg.version}/prebuilt-rag-index-gemini-${pkg.version}.tar.gz`
|
|
20
18
|
};
|
|
@@ -52,6 +50,7 @@ function normalizeGeminiModel(model) {
|
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
const profileConfig = resolveProfileConfig(process.env);
|
|
53
|
+
const defaultPrewarm = profileConfig.provider === "gemini";
|
|
55
54
|
|
|
56
55
|
const ragConfig = {
|
|
57
56
|
profile: profileConfig.profile,
|
|
@@ -62,8 +61,6 @@ const ragConfig = {
|
|
|
62
61
|
fallback: profileConfig.fallback,
|
|
63
62
|
cacheDir: readEnvValue("RAG_CACHE_DIR", join(dataRoot, ".rag-cache")),
|
|
64
63
|
modelCacheDir: readEnvValue("RAG_MODEL_CACHE_DIR", join(dataRoot, ".rag-cache", "models")),
|
|
65
|
-
localModel: readEnvValue("RAG_LOCAL_MODEL", "Xenova/all-MiniLM-L6-v2"),
|
|
66
|
-
localQuantized: readBoolEnv("RAG_LOCAL_QUANTIZED", true),
|
|
67
64
|
chunkSize: readIntEnv("RAG_CHUNK_SIZE", 1200),
|
|
68
65
|
chunkOverlap: readIntEnv("RAG_CHUNK_OVERLAP", 200),
|
|
69
66
|
maxChunksPerDoc: readIntEnv("RAG_MAX_CHUNKS_PER_DOC", 6),
|
|
@@ -71,11 +68,10 @@ const ragConfig = {
|
|
|
71
68
|
minScore: readFloatEnv("RAG_MIN_SCORE", 0.2),
|
|
72
69
|
includeScore: readBoolEnv("RAG_INCLUDE_SCORE", false),
|
|
73
70
|
rebuild: readBoolEnv("RAG_REBUILD", false),
|
|
74
|
-
prewarm: readBoolEnv("RAG_PREWARM",
|
|
71
|
+
prewarm: readBoolEnv("RAG_PREWARM", defaultPrewarm),
|
|
75
72
|
prewarmBlock: readBoolEnv("RAG_PREWARM_BLOCK", false),
|
|
76
73
|
prebuiltIndexAutoDownload: readBoolEnv("RAG_PREBUILT_INDEX_AUTO_DOWNLOAD", true),
|
|
77
74
|
prebuiltIndexUrl: readEnvValue("RAG_PREBUILT_INDEX_URL", ""),
|
|
78
|
-
prebuiltIndexUrlLocal: readEnvValue("RAG_PREBUILT_INDEX_URL_LOCAL", defaultPrebuiltIndexUrls.local),
|
|
79
75
|
prebuiltIndexUrlGemini: readEnvValue("RAG_PREBUILT_INDEX_URL_GEMINI", defaultPrebuiltIndexUrls.gemini),
|
|
80
76
|
prebuiltIndexTimeoutMs: readIntEnv("RAG_PREBUILT_INDEX_TIMEOUT_MS", 180000),
|
|
81
77
|
geminiApiKey: readEnvValue("GEMINI_API_KEY", ""),
|
package/src/rag/index.js
CHANGED
|
@@ -69,6 +69,29 @@ const providerOrchestrator = createProviderOrchestrator({
|
|
|
69
69
|
vectorCache
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
+
function classifyGeminiFailureReason(error) {
|
|
73
|
+
const status = Number(error?.status);
|
|
74
|
+
if (status === 401 || status === 403) return "invalid_auth";
|
|
75
|
+
if (status === 400 || status === 404) return "invalid_config";
|
|
76
|
+
const message = String(error?.message || "").toLowerCase();
|
|
77
|
+
if (message.includes("gemini_api_key") || message.includes("api key")) return "missing_api_key";
|
|
78
|
+
if (message.includes("embed model") || message.includes("model")) return "invalid_config";
|
|
79
|
+
return "runtime_error";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function logGeminiDegradedOnce({ reason, fallback, error, stage }) {
|
|
83
|
+
const key = `${stage}:${reason}:${fallback}`;
|
|
84
|
+
if (ragLogState.degradedNotices.has(key)) return;
|
|
85
|
+
ragLogState.degradedNotices.add(key);
|
|
86
|
+
logRag("provider_degraded", {
|
|
87
|
+
provider: "gemini",
|
|
88
|
+
fallback,
|
|
89
|
+
reason,
|
|
90
|
+
stage,
|
|
91
|
+
error: error?.message || String(error)
|
|
92
|
+
}, { level: "error" });
|
|
93
|
+
}
|
|
94
|
+
|
|
72
95
|
function refreshRagIndexes() {
|
|
73
96
|
providerOrchestrator.refreshProviders();
|
|
74
97
|
resetRagProviderLogState();
|
|
@@ -144,6 +167,13 @@ async function searchResources({ query, product, edition, platform, type, limit
|
|
|
144
167
|
fallback: ragConfig.fallback,
|
|
145
168
|
error: error.message
|
|
146
169
|
}, { level: "error" });
|
|
170
|
+
if (name === "gemini") {
|
|
171
|
+
const reason = classifyGeminiFailureReason(error);
|
|
172
|
+
const hasFallback = providers.includes("lexical") && providers[0] === "gemini";
|
|
173
|
+
if (hasFallback) {
|
|
174
|
+
logGeminiDegradedOnce({ reason, fallback: "lexical", error, stage: "search" });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
147
177
|
}
|
|
148
178
|
}
|
|
149
179
|
|
|
@@ -183,6 +213,10 @@ async function prewarmRagIndex() {
|
|
|
183
213
|
fallback: ragConfig.fallback
|
|
184
214
|
});
|
|
185
215
|
} catch (error) {
|
|
216
|
+
if (primary === "gemini" && providers.includes("lexical")) {
|
|
217
|
+
const reason = classifyGeminiFailureReason(error);
|
|
218
|
+
logGeminiDegradedOnce({ reason, fallback: "lexical", error, stage: "prewarm" });
|
|
219
|
+
}
|
|
186
220
|
logRag("prewarm_failed", {
|
|
187
221
|
provider: primary,
|
|
188
222
|
error: error.message
|
package/src/rag/logger.js
CHANGED
|
@@ -3,7 +3,7 @@ import { logEvent } from "../observability/logging.js";
|
|
|
3
3
|
const ragLogState = {
|
|
4
4
|
config: false,
|
|
5
5
|
providerChain: false,
|
|
6
|
-
|
|
6
|
+
degradedNotices: new Set(),
|
|
7
7
|
providerReady: new Set(),
|
|
8
8
|
providerFirstUse: new Set(),
|
|
9
9
|
fallbackUse: new Set()
|
|
@@ -23,7 +23,7 @@ function logRagConfigOnce(ragConfig) {
|
|
|
23
23
|
logRag(
|
|
24
24
|
`config provider=${ragConfig.provider} fallback=${ragConfig.fallback} prewarm=${ragConfig.prewarm} rebuild=${ragConfig.rebuild} ` +
|
|
25
25
|
`cache_dir=${ragConfig.cacheDir} prebuilt_auto_download=${ragConfig.prebuiltIndexAutoDownload} ` +
|
|
26
|
-
`prebuilt_url_override=${ragConfig.prebuiltIndexUrl ? "set" : "empty"}
|
|
26
|
+
`prebuilt_url_override=${ragConfig.prebuiltIndexUrl ? "set" : "empty"} ` +
|
|
27
27
|
`prebuilt_url_gemini=${ragConfig.prebuiltIndexUrlGemini ? "set" : "empty"} ` +
|
|
28
28
|
`prebuilt_timeout_ms=${ragConfig.prebuiltIndexTimeoutMs} gemini_retry_max_attempts=${ragConfig.geminiRetryMaxAttempts} ` +
|
|
29
29
|
`gemini_retry_base_delay_ms=${ragConfig.geminiRetryBaseDelayMs} gemini_retry_max_delay_ms=${ragConfig.geminiRetryMaxDelayMs} ` +
|
|
@@ -32,6 +32,7 @@ function logRagConfigOnce(ragConfig) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
function resetRagProviderLogState() {
|
|
35
|
+
ragLogState.degradedNotices.clear();
|
|
35
36
|
ragLogState.providerReady.clear();
|
|
36
37
|
ragLogState.providerFirstUse.clear();
|
|
37
38
|
ragLogState.fallbackUse.clear();
|
|
@@ -1,48 +1,50 @@
|
|
|
1
|
-
const PROFILE_DEFAULTS = {
|
|
2
|
-
lite: {
|
|
3
|
-
provider: "lexical",
|
|
4
|
-
fallback: "none"
|
|
5
|
-
},
|
|
6
|
-
"semantic-local": {
|
|
7
|
-
provider: "local",
|
|
8
|
-
fallback: "none"
|
|
9
|
-
},
|
|
10
|
-
"semantic-gemini": {
|
|
11
|
-
provider: "gemini",
|
|
12
|
-
fallback: "none"
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
|
|
16
1
|
function normalizeEnvValue(value) {
|
|
17
2
|
if (value === undefined || value === null) return "";
|
|
18
3
|
return String(value).trim().toLowerCase();
|
|
19
4
|
}
|
|
20
5
|
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const explicitFallback = normalizeEnvValue(env.RAG_FALLBACK);
|
|
6
|
+
function hasGeminiKey(env) {
|
|
7
|
+
return normalizeEnvValue(env?.GEMINI_API_KEY) !== "";
|
|
8
|
+
}
|
|
25
9
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
10
|
+
function resolveProvider(env) {
|
|
11
|
+
const explicit = normalizeEnvValue(env?.RAG_PROVIDER);
|
|
12
|
+
if (explicit === "gemini" || explicit === "lexical") {
|
|
13
|
+
return { value: explicit, source: "env" };
|
|
30
14
|
}
|
|
15
|
+
return {
|
|
16
|
+
value: hasGeminiKey(env) ? "gemini" : "lexical",
|
|
17
|
+
source: "auto"
|
|
18
|
+
};
|
|
19
|
+
}
|
|
31
20
|
|
|
32
|
-
|
|
33
|
-
const
|
|
21
|
+
function resolveFallback(env, provider) {
|
|
22
|
+
const explicit = normalizeEnvValue(env?.RAG_FALLBACK);
|
|
23
|
+
if (explicit === "none" || explicit === "gemini" || explicit === "lexical") {
|
|
24
|
+
return { value: explicit, source: "env" };
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
value: provider === "gemini" ? "lexical" : "none",
|
|
28
|
+
source: "auto"
|
|
29
|
+
};
|
|
30
|
+
}
|
|
34
31
|
|
|
32
|
+
function resolveProfileConfig(env = process.env) {
|
|
33
|
+
const provider = resolveProvider(env);
|
|
34
|
+
const fallback = resolveFallback(env, provider.value);
|
|
35
35
|
return {
|
|
36
|
-
profile,
|
|
37
|
-
defaults
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
profile: provider.value === "gemini" ? "semantic-gemini" : "lite",
|
|
37
|
+
defaults: {
|
|
38
|
+
provider: provider.value,
|
|
39
|
+
fallback: fallback.value
|
|
40
|
+
},
|
|
41
|
+
provider: provider.value,
|
|
42
|
+
fallback: fallback.value,
|
|
43
|
+
providerSource: provider.source,
|
|
44
|
+
fallbackSource: fallback.source
|
|
42
45
|
};
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
export {
|
|
46
|
-
PROFILE_DEFAULTS,
|
|
47
49
|
resolveProfileConfig
|
|
48
50
|
};
|
package/src/rag/providers.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { existsSync, mkdirSync } from "node:fs";
|
|
3
2
|
import { join } from "node:path";
|
|
4
3
|
import {
|
|
5
4
|
sleepMs,
|
|
@@ -10,16 +9,10 @@ import {
|
|
|
10
9
|
executeWithGeminiRetry
|
|
11
10
|
} from "./gemini-retry.js";
|
|
12
11
|
|
|
13
|
-
function ensureDirectory(path) {
|
|
14
|
-
if (!existsSync(path)) {
|
|
15
|
-
mkdirSync(path, { recursive: true });
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
12
|
function resolveProviderChain(ragConfig) {
|
|
20
13
|
let primary = ragConfig.provider;
|
|
21
14
|
if (primary === "auto") {
|
|
22
|
-
primary = ragConfig.geminiApiKey ? "gemini" : "
|
|
15
|
+
primary = ragConfig.geminiApiKey ? "gemini" : "lexical";
|
|
23
16
|
}
|
|
24
17
|
const chain = [primary];
|
|
25
18
|
if (ragConfig.fallback && ragConfig.fallback !== "none" && ragConfig.fallback !== primary) {
|
|
@@ -145,35 +138,8 @@ function createProviderOrchestrator({
|
|
|
145
138
|
}) {
|
|
146
139
|
let fuseSearch = utils.createFuseSearch(resourceIndex);
|
|
147
140
|
const providerCache = new Map();
|
|
148
|
-
let localEmbedderPromise = null;
|
|
149
141
|
let geminiEmbedderPromise = null;
|
|
150
142
|
|
|
151
|
-
async function getLocalEmbedder() {
|
|
152
|
-
if (localEmbedderPromise) return localEmbedderPromise;
|
|
153
|
-
localEmbedderPromise = (async () => {
|
|
154
|
-
const { pipeline, env } = await import("@xenova/transformers");
|
|
155
|
-
ensureDirectory(ragConfig.modelCacheDir);
|
|
156
|
-
if (!ragLogState.localEmbedderInit) {
|
|
157
|
-
ragLogState.localEmbedderInit = true;
|
|
158
|
-
logRag(
|
|
159
|
-
`init local embedder model=${ragConfig.localModel} quantized=${ragConfig.localQuantized} model_cache_dir=${ragConfig.modelCacheDir}`
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
env.cacheDir = ragConfig.modelCacheDir;
|
|
163
|
-
env.allowLocalModels = true;
|
|
164
|
-
const extractor = await pipeline("feature-extraction", ragConfig.localModel, {
|
|
165
|
-
quantized: ragConfig.localQuantized
|
|
166
|
-
});
|
|
167
|
-
return {
|
|
168
|
-
embed: async (text) => {
|
|
169
|
-
const output = await extractor(text, { pooling: "mean", normalize: true });
|
|
170
|
-
return Array.from(output.data);
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
})();
|
|
174
|
-
return localEmbedderPromise;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
143
|
async function getGeminiEmbedder() {
|
|
178
144
|
if (!ragConfig.geminiApiKey) {
|
|
179
145
|
throw new Error("GEMINI_API_KEY is required for gemini embeddings.");
|
|
@@ -537,16 +503,6 @@ function createProviderOrchestrator({
|
|
|
537
503
|
entryMatchesScope: utils.entryMatchesScope,
|
|
538
504
|
attachScore: utils.attachScore
|
|
539
505
|
}));
|
|
540
|
-
} else if (name === "local") {
|
|
541
|
-
providerPromise = (async () => {
|
|
542
|
-
const embedder = await getLocalEmbedder();
|
|
543
|
-
return createVectorProvider({
|
|
544
|
-
name: "local",
|
|
545
|
-
model: ragConfig.localModel,
|
|
546
|
-
embedder,
|
|
547
|
-
batchSize: 1
|
|
548
|
-
});
|
|
549
|
-
})();
|
|
550
506
|
} else if (name === "gemini") {
|
|
551
507
|
providerPromise = (async () => {
|
|
552
508
|
const embedder = await getGeminiEmbedder();
|
package/src/rag/vector-cache.js
CHANGED
|
@@ -154,9 +154,7 @@ function resolvePrebuiltIndexUrlCandidates(provider, ragConfig, legacyPrebuiltIn
|
|
|
154
154
|
if (override) return [override];
|
|
155
155
|
|
|
156
156
|
const candidates = [];
|
|
157
|
-
if (provider === "
|
|
158
|
-
candidates.push(String(ragConfig.prebuiltIndexUrlLocal || "").trim());
|
|
159
|
-
} else if (provider === "gemini") {
|
|
157
|
+
if (provider === "gemini") {
|
|
160
158
|
candidates.push(String(ragConfig.prebuiltIndexUrlGemini || "").trim());
|
|
161
159
|
}
|
|
162
160
|
candidates.push(legacyPrebuiltIndexUrl);
|
|
@@ -204,7 +202,7 @@ function createVectorCacheHelpers({ ragConfig, pkgVersion, legacyPrebuiltIndexUr
|
|
|
204
202
|
const prebuiltDownloadAttempts = new Map();
|
|
205
203
|
|
|
206
204
|
async function maybeDownloadPrebuiltVectorIndex({ provider, model, cacheKey, signature, cacheFile }) {
|
|
207
|
-
if (
|
|
205
|
+
if (provider !== "gemini") {
|
|
208
206
|
return { downloaded: false, reason: "provider_not_supported" };
|
|
209
207
|
}
|
|
210
208
|
if (!ragConfig.prebuiltIndexAutoDownload) {
|