Skip to content

Commit 30fb596

Browse files
committed
feat(ai): Implement complete openAiCompatible provider architecture (#9766)
- Refactored TextEmbeddingService.mjs to support native /v1/embeddings targeting MLX endpoints using ES6 optional chaining - Documented openAiCompatible architecture universally inside JSDocs and configurations - Safely added apiKey endpoint mapping via standard dot-env security practices - Finalized MLX/OpenAI compatibility inside AI quick start documentation
1 parent b499e71 commit 30fb596

4 files changed

Lines changed: 72 additions & 7 deletions

File tree

.github/AI_QUICK_START.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,11 @@ To ensure your environment is accurately inherited across all local MCP sub-serv
8383
```bash
8484
export GEMINI_API_KEY="YOUR_API_KEY_HERE"
8585
export GH_TOKEN="YOUR_GITHUB_TOKEN_HERE"
86-
export NEO_EMBEDDING_PROVIDER="gemini" # or "ollama" for 100% offline
86+
# Core LLM Engine provider (Supported: 'gemini', 'ollama', 'openAiCompatible')
87+
# export MODEL_PROVIDER="openAiCompatible"
88+
89+
# Vector Embedding provider (Supported: 'gemini', 'ollama', 'openAiCompatible')
90+
export NEO_EMBEDDING_PROVIDER="gemini"
8791
```
8892
3. **Apply changes**: `source ~/.zshrc`
8993

@@ -101,8 +105,8 @@ The free tier of the Gemini API has a strict limit of **1,000 requests per day**
101105
* **Do NOT** run `npm run ai:sync-kb` (full rebuild) unless absolutely necessary. A full rebuild requires ~153 requests and takes ~25 minutes due to rate-limiting delays.
102106
* The pre-built artifact saves you from this cost and delay.
103107

104-
**Local Architecture (Ollama Integration):**
105-
To completely bypass Gemini API limits and operate a 100% offline knowledge base, you can utilize the local inference loop. Export `NEO_EMBEDDING_PROVIDER="ollama"` in your environment variables. Ensure the corresponding Ollama embedding models are running locally.
108+
**Local Architecture (Ollama & MLX / OpenAI-Compatible):**
109+
To completely bypass Gemini API limits and operate a 100% offline knowledge base, you can utilize the local inference loops. Export `NEO_EMBEDDING_PROVIDER="ollama"` or `NEO_EMBEDDING_PROVIDER="openAiCompatible"` in your environment variables. Ensure the corresponding embedding models and servers (e.g. `mlx_lm.server`) are running locally.
106110

107111
### Step 3.5: Advanced Configuration (Optional)
108112

ai/mcp/server/memory-core/config.template.mjs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,20 @@ const defaultConfig = {
6363
},
6464
/**
6565
* Explicit override provider for the core LLM Engine (e.g. summarization).
66-
* @type {string}
66+
* Supported values: 'gemini', 'ollama', 'openAiCompatible'
67+
* @type {String}
6768
*/
6869
modelProvider: process.env.MODEL_PROVIDER || 'gemini',
6970
/**
7071
* Explicit override provider for the SQLite Native Database Engine.
71-
* @type {string}
72+
* Supported values: 'gemini', 'ollama', 'openAiCompatible'
73+
* @type {String}
7274
*/
7375
neoEmbeddingProvider: process.env.NEO_EMBEDDING_PROVIDER || 'gemini',
7476
/**
7577
* Explicit override provider for the ChromaDB Engine.
76-
* @type {string}
78+
* Supported values: 'gemini', 'ollama', 'openAiCompatible'
79+
* @type {String}
7780
*/
7881
chromaEmbeddingProvider: process.env.CHROMA_EMBEDDING_PROVIDER || 'gemini',
7982
/**
@@ -86,11 +89,13 @@ const defaultConfig = {
8689
},
8790
/**
8891
* Settings for the OpenAI-Compatible API integration (e.g., mlx-lm or mlx-openai-server)
92+
* WARNING: Never hardcode API keys here. Always export them via .env or globally.
8993
*/
9094
openAiCompatible: {
9195
host : process.env.OPENAI_COMPATIBLE_HOST || 'http://127.0.0.1:8000',
9296
model : process.env.OPENAI_COMPATIBLE_MODEL || 'gemma4:31b',
93-
embeddingModel: process.env.OPENAI_COMPATIBLE_EMBEDDING_MODEL || 'qwen3-embedding'
97+
embeddingModel: process.env.OPENAI_COMPATIBLE_EMBEDDING_MODEL || 'qwen3-embedding',
98+
apiKey : process.env.OPENAI_COMPATIBLE_API_KEY || ''
9499
},
95100
/**
96101
* The name of the Google Generative AI model for content generation.

ai/mcp/server/memory-core/services/TextEmbeddingService.mjs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,59 @@ class TextEmbeddingService extends Base {
108108
logger.error(`[TextEmbeddingService] Failed to generate embedding from Ollama:`, err.message);
109109
throw err;
110110
}
111+
} else if (explicitProvider === 'openAiCompatible') {
112+
const { host, embeddingModel, apiKey } = aiConfig.openAiCompatible;
113+
try {
114+
const parsedUrl = new URL(`${host}/v1/embeddings`);
115+
const httpModule = parsedUrl.protocol === 'https:' ? await import('https') : await import('http');
116+
117+
let resolveFunc, rejectFunc;
118+
const responsePromise = new Promise((res, rej) => {
119+
resolveFunc = res;
120+
rejectFunc = rej;
121+
});
122+
123+
const reqHeaders = { 'Content-Type': 'application/json' };
124+
if (apiKey) {
125+
reqHeaders.Authorization = `Bearer ${apiKey}`;
126+
}
127+
128+
const req = httpModule.request(parsedUrl, {
129+
method : 'POST',
130+
headers: reqHeaders,
131+
timeout: 60 * 60 * 1000 // 1 hour timeout natively
132+
}, (res) => {
133+
let body = '';
134+
res.on('data', chunk => body += chunk);
135+
res.on('end', () => {
136+
if (res.statusCode < 200 || res.statusCode >= 300) {
137+
rejectFunc(new Error(`openAiCompatible embedding error HTTP ${res.statusCode}: ${body}`));
138+
} else {
139+
try {
140+
const result = JSON.parse(body);
141+
resolveFunc(result);
142+
} catch (e) {
143+
rejectFunc(new Error(`Failed to parse openAiCompatible response: ${e.message}`));
144+
}
145+
}
146+
});
147+
});
148+
149+
req.on('error', (err) => rejectFunc(err));
150+
req.on('timeout', () => {
151+
req.destroy();
152+
rejectFunc(new Error('openAiCompatible request timed out after 1 hour'));
153+
});
154+
155+
req.write(JSON.stringify({ model: embeddingModel, input: text }));
156+
req.end();
157+
158+
const result = await responsePromise;
159+
return result.data?.[0]?.embedding;
160+
} catch (err) {
161+
logger.error(`[TextEmbeddingService] Failed to generate embedding from openAiCompatible:`, err.message);
162+
throw err;
163+
}
111164
} else {
112165
if (!process.env.GEMINI_API_KEY) {
113166
throw new Error('Semantic search unavailable: GEMINI_API_KEY is missing.');

resources/content/sandman_handoff.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,6 @@ Based on the latest Tri-Vector Synthesis and Topological Priorities, the followi
3434
- **[Codebase Gap]** Node `Ticket #8462`: [DOC_GAP] No files related to 'SectionsList' were found in the directory tree. There is a complete lack of evidence for existing documentation or test coverage regarding the styling and implementation of the SectionsList component. (Source Session: fd1a1232-8923-469d-990b-73b00efd1ef1)
3535
- **[Codebase Gap]** Node `.neo-list-item`: [DOC_GAP] The CSS class `.neo-list-item` intended for `SectionsList` is not documented in the `docs/` or `learn/` directories, and there are no corresponding tests in `test/playwright/` to verify the rendering or styling of these list items. (Source Session: fd1a1232-8923-469d-990b-73b00efd1ef1)
3636
- **[Codebase Gap]** Node `Issue #8620`: [DOC_GAP] No functional tests or documentation found for Element.moveBefore support. The provided directory tree contains only a workflow test file (OpenapiIssues.spec.mjs), which does not cover the implementation or usage of the Element.moveBefore method. (Source Session: fd130b21-2cfe-4230-b3ba-4d2cd527d2d2)
37+
- **[Codebase Gap]** Node `src/state/Provider.mjs`: [DOC_GAP] While comprehensive guides exist for reactivity and usage, there is no documentation regarding 'store serialization' specifically mentioned in the node description. Additionally, no corresponding test files were found in the provided directory tree for src/state/Provider.mjs. (Source Session: fd099a94-e4dc-4780-ab47-f2f91685aaf2)
38+
- **[Codebase Gap]** Node `src/layout/Base.mjs`: [DOC_GAP] src/layout/Base.mjs (Base layout class) lacks native test coverage and developer documentation. While 'learn/guides/uibuildingblocks/Layouts.md' exists, it is a user guide for applying layouts via configuration and does not document the internal serialization logic or the Base class API for developers. (Source Session: fd099a94-e4dc-4780-ab47-f2f91685aaf2)
39+
- **[Codebase Gap]** Node `src/layout/Flexbox.mjs`: [DOC_GAP] The 'gap' property mentioned in the Flexbox.mjs description (prefix and gap serialization) is missing from the documentation in 'learn/guides/uibuildingblocks/Layouts.md', which only covers basic align and pack properties for vbox/hbox. Additionally, there are no corresponding test files for the layout implementation in the provided directory tree. (Source Session: fd099a94-e4dc-4780-ab47-f2f91685aaf2)

0 commit comments

Comments
 (0)