voyageai-cli 1.24.0 → 1.26.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/package.json +1 -1
- package/src/cli.js +2 -0
- package/src/commands/about.js +1 -1
- package/src/commands/bug.js +1 -1
- package/src/commands/chat.js +281 -78
- package/src/commands/playground.js +73 -19
- package/src/commands/scaffold.js +23 -1
- package/src/commands/workflow.js +336 -0
- package/src/lib/chat.js +170 -4
- package/src/lib/explanations.js +53 -0
- package/src/lib/llm.js +304 -2
- package/src/lib/mongo.js +6 -6
- package/src/lib/prompt.js +60 -1
- package/src/lib/scaffold-structure.js +8 -9
- package/src/lib/telemetry.js +1 -1
- package/src/lib/template-engine.js +240 -0
- package/src/lib/templates/nextjs/README.md.tpl +78 -55
- package/src/lib/templates/nextjs/favicon.svg.tpl +11 -0
- package/src/lib/templates/nextjs/footer.jsx.tpl +49 -0
- package/src/lib/templates/nextjs/layout.jsx.tpl +16 -10
- package/src/lib/templates/nextjs/lib-mongo.js.tpl +5 -5
- package/src/lib/templates/nextjs/lib-voyage.js.tpl +13 -8
- package/src/lib/templates/nextjs/navbar.jsx.tpl +98 -0
- package/src/lib/templates/nextjs/page-home.jsx.tpl +201 -0
- package/src/lib/templates/nextjs/page-search.jsx.tpl +184 -82
- package/src/lib/templates/nextjs/theme-registry.jsx.tpl +51 -0
- package/src/lib/templates/nextjs/theme.js.tpl +138 -65
- package/src/lib/templates/nextjs/vai-logo-256.png +0 -0
- package/src/lib/tool-registry.js +194 -0
- package/src/lib/workflow-utils.js +65 -0
- package/src/lib/workflow.js +1259 -0
- package/src/mcp/tools/embedding.js +55 -43
- package/src/mcp/tools/ingest.js +74 -67
- package/src/mcp/tools/management.js +54 -101
- package/src/mcp/tools/retrieval.js +181 -163
- package/src/mcp/tools/utility.js +171 -153
- package/src/playground/icons/dark/128.png +0 -0
- package/src/playground/icons/dark/16.png +0 -0
- package/src/playground/icons/dark/256.png +0 -0
- package/src/playground/icons/dark/32.png +0 -0
- package/src/playground/icons/dark/64.png +0 -0
- package/src/playground/icons/light/128.png +0 -0
- package/src/playground/icons/light/16.png +0 -0
- package/src/playground/icons/light/256.png +0 -0
- package/src/playground/icons/light/32.png +0 -0
- package/src/playground/icons/light/64.png +0 -0
- package/src/playground/icons/watermark.png +0 -0
- package/src/playground/index.html +633 -83
- package/src/workflows/consistency-check.json +64 -0
- package/src/workflows/cost-analysis.json +69 -0
- package/src/workflows/multi-collection-search.json +80 -0
- package/src/workflows/research-and-summarize.json +46 -0
- package/src/workflows/smart-ingest.json +63 -0
|
@@ -1,84 +1,157 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MUI Theme Configuration
|
|
2
|
+
* MUI Theme Configuration — vai branded
|
|
3
3
|
* Generated by vai v{{vaiVersion}} on {{generatedAt}}
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
'use client';
|
|
7
7
|
|
|
8
|
-
import { createTheme } from '@mui/material/styles';
|
|
8
|
+
import { createTheme, responsiveFontSizes } from '@mui/material/styles';
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
default: '#FAFAFA',
|
|
26
|
-
paper: '#FFFFFF',
|
|
27
|
-
},
|
|
28
|
-
text: {
|
|
29
|
-
primary: '#001E2B',
|
|
30
|
-
secondary: '#5C6C75',
|
|
31
|
-
},
|
|
10
|
+
/* ── colour tokens ─────────────────────────────── */
|
|
11
|
+
const brand = {
|
|
12
|
+
green: '#00ED64',
|
|
13
|
+
greenLight: '#71F6A0',
|
|
14
|
+
greenDark: '#00B84A',
|
|
15
|
+
navy: '#001E2B',
|
|
16
|
+
navyLight: '#1C3A4B',
|
|
17
|
+
slate: '#3D4F58',
|
|
18
|
+
mist: '#E3FCF7',
|
|
19
|
+
white: '#FFFFFF',
|
|
20
|
+
offWhite: '#F9FBFA',
|
|
21
|
+
grey100: '#F5F6F7',
|
|
22
|
+
grey300: '#C1C7C6',
|
|
23
|
+
grey500: '#889397',
|
|
24
|
+
grey700: '#5C6C75',
|
|
32
25
|
};
|
|
33
26
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
27
|
+
/* ── light palette ─────────────────────────────── */
|
|
28
|
+
const lightPalette = {
|
|
29
|
+
mode: 'light',
|
|
30
|
+
primary: { main: brand.green, light: brand.greenLight, dark: brand.greenDark, contrastText: brand.navy },
|
|
31
|
+
secondary: { main: brand.navy, light: brand.navyLight, dark: '#000F17', contrastText: brand.white },
|
|
32
|
+
background: { default: brand.offWhite, paper: brand.white },
|
|
33
|
+
text: { primary: brand.navy, secondary: brand.grey700 },
|
|
34
|
+
divider: brand.grey300,
|
|
35
|
+
success: { main: brand.greenDark },
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/* ── dark palette ──────────────────────────────── */
|
|
39
|
+
const darkPalette = {
|
|
40
|
+
mode: 'dark',
|
|
41
|
+
primary: { main: brand.green, light: brand.greenLight, dark: brand.greenDark, contrastText: brand.navy },
|
|
42
|
+
secondary: { main: '#90CAF9', light: '#BBDEFB', dark: '#42A5F5', contrastText: brand.navy },
|
|
43
|
+
background: { default: '#0A1117', paper: '#111D27' },
|
|
44
|
+
text: { primary: '#E8EDDF', secondary: '#9AABB8' },
|
|
45
|
+
divider: 'rgba(255,255,255,0.12)',
|
|
46
|
+
success: { main: brand.green },
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
function buildTheme(mode = 'light') {
|
|
50
|
+
let theme = createTheme({
|
|
51
|
+
palette: mode === 'dark' ? darkPalette : lightPalette,
|
|
52
|
+
typography: {
|
|
53
|
+
fontFamily: '"Inter", "IBM Plex Sans", "Roboto", "Helvetica Neue", Arial, sans-serif',
|
|
54
|
+
h1: { fontWeight: 800, letterSpacing: '-0.02em' },
|
|
55
|
+
h2: { fontWeight: 700, letterSpacing: '-0.01em' },
|
|
56
|
+
h3: { fontWeight: 700 },
|
|
57
|
+
h4: { fontWeight: 700 },
|
|
58
|
+
h5: { fontWeight: 600 },
|
|
59
|
+
h6: { fontWeight: 600 },
|
|
60
|
+
subtitle1: { fontWeight: 500, color: mode === 'dark' ? '#9AABB8' : brand.grey700 },
|
|
61
|
+
button: { fontWeight: 600, textTransform: 'none' },
|
|
62
|
+
overline: { fontWeight: 700, letterSpacing: '0.08em' },
|
|
49
63
|
},
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
shape: { borderRadius: 12 },
|
|
65
|
+
shadows: [
|
|
66
|
+
'none',
|
|
67
|
+
'0 1px 2px rgba(0,0,0,0.06)',
|
|
68
|
+
'0 1px 4px rgba(0,0,0,0.08)',
|
|
69
|
+
'0 2px 8px rgba(0,0,0,0.10)',
|
|
70
|
+
'0 4px 12px rgba(0,0,0,0.10)',
|
|
71
|
+
'0 6px 16px rgba(0,0,0,0.10)',
|
|
72
|
+
...Array(19).fill('0 8px 24px rgba(0,0,0,0.12)'),
|
|
73
|
+
],
|
|
74
|
+
components: {
|
|
75
|
+
MuiCssBaseline: {
|
|
76
|
+
styleOverrides: {
|
|
77
|
+
body: {
|
|
78
|
+
scrollbarWidth: 'thin',
|
|
79
|
+
'&::-webkit-scrollbar': { width: 6 },
|
|
80
|
+
'&::-webkit-scrollbar-thumb': { background: brand.grey300, borderRadius: 3 },
|
|
81
|
+
},
|
|
60
82
|
},
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
83
|
+
},
|
|
84
|
+
MuiButton: {
|
|
85
|
+
defaultProps: { disableElevation: true },
|
|
86
|
+
styleOverrides: {
|
|
87
|
+
root: { borderRadius: 8, padding: '8px 20px', fontWeight: 600 },
|
|
88
|
+
containedPrimary: {
|
|
89
|
+
color: brand.navy,
|
|
90
|
+
'&:hover': { background: brand.greenLight },
|
|
65
91
|
},
|
|
92
|
+
outlined: { borderWidth: 2, '&:hover': { borderWidth: 2 } },
|
|
66
93
|
},
|
|
67
94
|
},
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
95
|
+
MuiCard: {
|
|
96
|
+
styleOverrides: {
|
|
97
|
+
root: {
|
|
98
|
+
borderRadius: 16,
|
|
99
|
+
border: `1px solid ${mode === 'dark' ? 'rgba(255,255,255,0.08)' : brand.grey100}`,
|
|
100
|
+
transition: 'box-shadow 0.2s ease, transform 0.2s ease',
|
|
101
|
+
'&:hover': {
|
|
102
|
+
boxShadow: mode === 'dark'
|
|
103
|
+
? '0 8px 24px rgba(0,237,100,0.08)'
|
|
104
|
+
: '0 8px 24px rgba(0,0,0,0.08)',
|
|
105
|
+
transform: 'translateY(-2px)',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
73
108
|
},
|
|
74
109
|
},
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
110
|
+
MuiChip: {
|
|
111
|
+
styleOverrides: {
|
|
112
|
+
root: { fontWeight: 600, borderRadius: 8 },
|
|
113
|
+
colorSuccess: {
|
|
114
|
+
background: mode === 'dark' ? 'rgba(0,237,100,0.15)' : brand.mist,
|
|
115
|
+
color: mode === 'dark' ? brand.green : brand.greenDark,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
MuiTextField: {
|
|
120
|
+
styleOverrides: {
|
|
121
|
+
root: {
|
|
122
|
+
'& .MuiOutlinedInput-root': {
|
|
123
|
+
borderRadius: 12,
|
|
124
|
+
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
|
125
|
+
borderColor: brand.green,
|
|
126
|
+
borderWidth: 2,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
MuiAppBar: {
|
|
133
|
+
styleOverrides: {
|
|
134
|
+
root: {
|
|
135
|
+
background: mode === 'dark' ? '#0D1820' : brand.white,
|
|
136
|
+
borderBottom: `1px solid ${mode === 'dark' ? 'rgba(255,255,255,0.08)' : brand.grey100}`,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
MuiLinearProgress: {
|
|
141
|
+
styleOverrides: {
|
|
142
|
+
root: { borderRadius: 4, height: 6 },
|
|
143
|
+
bar: { borderRadius: 4 },
|
|
80
144
|
},
|
|
81
145
|
},
|
|
82
146
|
},
|
|
83
|
-
}
|
|
84
|
-
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
theme = responsiveFontSizes(theme);
|
|
150
|
+
return theme;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export const lightTheme = buildTheme('light');
|
|
154
|
+
export const darkTheme = buildTheme('dark');
|
|
155
|
+
|
|
156
|
+
/* default export for simple usage */
|
|
157
|
+
export const theme = lightTheme;
|
|
Binary file
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tool Registry
|
|
5
|
+
*
|
|
6
|
+
* Single source of truth mapping MCP Zod schemas to LLM tool definitions
|
|
7
|
+
* and dispatching tool execution. Bridges the MCP tool handlers with the
|
|
8
|
+
* agent chat loop.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { z } = require('zod');
|
|
12
|
+
const schemas = require('../mcp/schemas');
|
|
13
|
+
|
|
14
|
+
// Lazy-loaded handlers to avoid circular deps
|
|
15
|
+
let _handlers;
|
|
16
|
+
function getHandlers() {
|
|
17
|
+
if (!_handlers) {
|
|
18
|
+
const { handleVaiQuery, handleVaiSearch, handleVaiRerank } = require('../mcp/tools/retrieval');
|
|
19
|
+
const { handleVaiEmbed, handleVaiSimilarity } = require('../mcp/tools/embedding');
|
|
20
|
+
const { handleVaiCollections, handleVaiModels } = require('../mcp/tools/management');
|
|
21
|
+
const { handleVaiTopics, handleVaiExplain, handleVaiEstimate } = require('../mcp/tools/utility');
|
|
22
|
+
const { handleVaiIngest } = require('../mcp/tools/ingest');
|
|
23
|
+
|
|
24
|
+
_handlers = {
|
|
25
|
+
vai_query: handleVaiQuery,
|
|
26
|
+
vai_search: handleVaiSearch,
|
|
27
|
+
vai_rerank: handleVaiRerank,
|
|
28
|
+
vai_embed: handleVaiEmbed,
|
|
29
|
+
vai_similarity: handleVaiSimilarity,
|
|
30
|
+
vai_collections: handleVaiCollections,
|
|
31
|
+
vai_models: handleVaiModels,
|
|
32
|
+
vai_topics: handleVaiTopics,
|
|
33
|
+
vai_explain: handleVaiExplain,
|
|
34
|
+
vai_estimate: handleVaiEstimate,
|
|
35
|
+
vai_ingest: handleVaiIngest,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return _handlers;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Tool definitions: name, description, and schema key for each tool.
|
|
43
|
+
*/
|
|
44
|
+
const TOOL_DEFINITIONS = [
|
|
45
|
+
{
|
|
46
|
+
name: 'vai_query',
|
|
47
|
+
description: 'Full RAG query: embeds the question with Voyage AI, runs vector search against MongoDB Atlas, and reranks results. Use this when you need to answer a question using the knowledge base.',
|
|
48
|
+
schemaKey: 'querySchema',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'vai_search',
|
|
52
|
+
description: 'Raw vector similarity search without reranking. Faster than vai_query but results are ordered by vector distance only. Use for exploratory searches or when you plan to rerank separately.',
|
|
53
|
+
schemaKey: 'searchSchema',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'vai_rerank',
|
|
57
|
+
description: 'Rerank documents against a query using Voyage AI reranker. Takes a query and candidate documents, returns them reordered by relevance.',
|
|
58
|
+
schemaKey: 'rerankSchema',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'vai_embed',
|
|
62
|
+
description: 'Embed text using a Voyage AI model and return the vector representation. Use for custom similarity logic, storing vectors, or debugging.',
|
|
63
|
+
schemaKey: 'embedSchema',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'vai_similarity',
|
|
67
|
+
description: 'Compare two texts semantically by embedding both and computing cosine similarity. Returns a score from -1 to 1.',
|
|
68
|
+
schemaKey: 'similaritySchema',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'vai_collections',
|
|
72
|
+
description: 'List available MongoDB collections with document counts and vector index information. Use to discover which knowledge bases exist.',
|
|
73
|
+
schemaKey: 'collectionsSchema',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'vai_models',
|
|
77
|
+
description: 'List available Voyage AI models with capabilities and pricing. Use when selecting a model or comparing options.',
|
|
78
|
+
schemaKey: 'modelsSchema',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'vai_topics',
|
|
82
|
+
description: 'List all available educational topics. Call this to discover what vai can explain.',
|
|
83
|
+
schemaKey: 'topicsSchema',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'vai_explain',
|
|
87
|
+
description: 'Get a detailed explanation of a topic (embeddings, vector search, RAG, MoE, etc). Supports fuzzy matching.',
|
|
88
|
+
schemaKey: 'explainSchema',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'vai_estimate',
|
|
92
|
+
description: 'Estimate costs for Voyage AI embedding and query operations at various scales.',
|
|
93
|
+
schemaKey: 'estimateSchema',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'vai_ingest',
|
|
97
|
+
description: 'Add a document to a collection: chunks the text, embeds each chunk with Voyage AI, and stores in MongoDB Atlas.',
|
|
98
|
+
schemaKey: 'ingestSchema',
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Convert a Zod schema fields object (as used in MCP schemas) to JSON Schema.
|
|
104
|
+
* Strips fields with defaults from the required array so the LLM doesn't
|
|
105
|
+
* have to provide them.
|
|
106
|
+
*
|
|
107
|
+
* @param {object} zodFields - Plain object of Zod field definitions
|
|
108
|
+
* @returns {object} JSON Schema object
|
|
109
|
+
*/
|
|
110
|
+
function zodSchemaToJsonSchema(zodFields) {
|
|
111
|
+
const obj = z.object(zodFields);
|
|
112
|
+
const jsonSchema = z.toJSONSchema(obj);
|
|
113
|
+
|
|
114
|
+
// Remove $schema key (not needed for tool definitions)
|
|
115
|
+
delete jsonSchema['$schema'];
|
|
116
|
+
|
|
117
|
+
// Strip fields with 'default' from required array.
|
|
118
|
+
// LLMs should not be forced to provide values that have defaults.
|
|
119
|
+
if (jsonSchema.required && jsonSchema.properties) {
|
|
120
|
+
jsonSchema.required = jsonSchema.required.filter(key => {
|
|
121
|
+
const prop = jsonSchema.properties[key];
|
|
122
|
+
return prop && !('default' in prop);
|
|
123
|
+
});
|
|
124
|
+
if (jsonSchema.required.length === 0) delete jsonSchema.required;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return jsonSchema;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get tool definitions formatted for a specific LLM provider.
|
|
132
|
+
*
|
|
133
|
+
* @param {'anthropic'|'openai'|'ollama'} format - Provider format
|
|
134
|
+
* @returns {Array} Tool definitions in provider-specific format
|
|
135
|
+
*/
|
|
136
|
+
function getToolDefinitions(format) {
|
|
137
|
+
return TOOL_DEFINITIONS.map(def => {
|
|
138
|
+
const zodFields = schemas[def.schemaKey];
|
|
139
|
+
const inputSchema = zodSchemaToJsonSchema(zodFields);
|
|
140
|
+
|
|
141
|
+
if (format === 'anthropic') {
|
|
142
|
+
return {
|
|
143
|
+
name: def.name,
|
|
144
|
+
description: def.description,
|
|
145
|
+
input_schema: inputSchema,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// OpenAI / Ollama format
|
|
150
|
+
return {
|
|
151
|
+
type: 'function',
|
|
152
|
+
function: {
|
|
153
|
+
name: def.name,
|
|
154
|
+
description: def.description,
|
|
155
|
+
parameters: inputSchema,
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Execute a tool by name with the given arguments.
|
|
163
|
+
* Validates args against the Zod schema, then calls the handler.
|
|
164
|
+
*
|
|
165
|
+
* @param {string} name - Tool name (e.g. 'vai_query')
|
|
166
|
+
* @param {object} args - Tool arguments
|
|
167
|
+
* @returns {Promise<{structuredContent: object, content: Array}>}
|
|
168
|
+
*/
|
|
169
|
+
async function executeTool(name, args) {
|
|
170
|
+
const handlers = getHandlers();
|
|
171
|
+
const handler = handlers[name];
|
|
172
|
+
if (!handler) {
|
|
173
|
+
throw new Error(`Unknown tool: "${name}". Available: ${Object.keys(handlers).join(', ')}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Find the schema for validation
|
|
177
|
+
const def = TOOL_DEFINITIONS.find(d => d.name === name);
|
|
178
|
+
if (!def) throw new Error(`No schema found for tool: "${name}"`);
|
|
179
|
+
|
|
180
|
+
const zodFields = schemas[def.schemaKey];
|
|
181
|
+
const zodObj = z.object(zodFields);
|
|
182
|
+
|
|
183
|
+
// Validate and apply defaults
|
|
184
|
+
const validated = zodObj.parse(args);
|
|
185
|
+
|
|
186
|
+
return handler(validated);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
module.exports = {
|
|
190
|
+
TOOL_DEFINITIONS,
|
|
191
|
+
zodSchemaToJsonSchema,
|
|
192
|
+
getToolDefinitions,
|
|
193
|
+
executeTool,
|
|
194
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { requireMongoUri } = require('./mongo');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Introspect MongoDB collections: list collections with vector index info.
|
|
7
|
+
* Moved from src/mcp/tools/management.js for reuse by the workflow engine.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} dbName
|
|
10
|
+
* @returns {Promise<Array<{ name: string, documentCount: number, hasVectorIndex: boolean, embeddingField?: string, dimensions?: number }>>}
|
|
11
|
+
*/
|
|
12
|
+
async function introspectCollections(dbName) {
|
|
13
|
+
const { MongoClient } = require('mongodb');
|
|
14
|
+
const uri = requireMongoUri();
|
|
15
|
+
const client = new MongoClient(uri);
|
|
16
|
+
await client.connect();
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const db = client.db(dbName);
|
|
20
|
+
const collections = await db.listCollections().toArray();
|
|
21
|
+
const results = [];
|
|
22
|
+
|
|
23
|
+
for (const collInfo of collections) {
|
|
24
|
+
if (collInfo.name.startsWith('system.')) continue;
|
|
25
|
+
const coll = db.collection(collInfo.name);
|
|
26
|
+
const documentCount = await coll.estimatedDocumentCount();
|
|
27
|
+
|
|
28
|
+
let hasVectorIndex = false;
|
|
29
|
+
let embeddingField;
|
|
30
|
+
let dimensions;
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const indexes = await coll.listSearchIndexes().toArray();
|
|
34
|
+
for (const idx of indexes) {
|
|
35
|
+
const fields = idx.latestDefinition?.fields || [];
|
|
36
|
+
for (const f of fields) {
|
|
37
|
+
if (f.type === 'vector') {
|
|
38
|
+
hasVectorIndex = true;
|
|
39
|
+
embeddingField = f.path;
|
|
40
|
+
dimensions = f.numDimensions;
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (hasVectorIndex) break;
|
|
45
|
+
}
|
|
46
|
+
} catch {
|
|
47
|
+
// listSearchIndexes may not be available on non-Atlas deployments
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
results.push({
|
|
51
|
+
name: collInfo.name,
|
|
52
|
+
documentCount,
|
|
53
|
+
hasVectorIndex,
|
|
54
|
+
...(embeddingField && { embeddingField }),
|
|
55
|
+
...(dimensions && { dimensions }),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return results;
|
|
60
|
+
} finally {
|
|
61
|
+
await client.close();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = { introspectCollections };
|