@open-mercato/ai-assistant 0.6.6-develop.6339.1.193c6c7c71 → 0.6.6-develop.6344.1.c4b17c07c4
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/dist/modules/ai_assistant/cli.js +3 -3
- package/dist/modules/ai_assistant/cli.js.map +2 -2
- package/dist/modules/ai_assistant/lib/entity-graph.js +1 -1
- package/dist/modules/ai_assistant/lib/entity-graph.js.map +2 -2
- package/dist/modules/ai_assistant/lib/entity-index-config.js +1 -1
- package/dist/modules/ai_assistant/lib/entity-index-config.js.map +2 -2
- package/dist/modules/ai_assistant/lib/openai-compatible-presets.js +16 -0
- package/dist/modules/ai_assistant/lib/openai-compatible-presets.js.map +2 -2
- package/dist/modules/ai_assistant/lib/tool-index-config.js +1 -1
- package/dist/modules/ai_assistant/lib/tool-index-config.js.map +2 -2
- package/package.json +6 -6
- package/src/modules/ai_assistant/cli.ts +3 -3
- package/src/modules/ai_assistant/lib/entity-graph.ts +1 -1
- package/src/modules/ai_assistant/lib/entity-index-config.ts +1 -1
- package/src/modules/ai_assistant/lib/openai-compatible-presets.ts +22 -0
- package/src/modules/ai_assistant/lib/tool-index-config.ts +1 -1
|
@@ -152,11 +152,11 @@ Registered MCP Tools (${toolNames.length}):
|
|
|
152
152
|
list.push(name);
|
|
153
153
|
byModule.set(module, list);
|
|
154
154
|
}
|
|
155
|
-
const sortedModules = Array.from(byModule.keys()).sort();
|
|
155
|
+
const sortedModules = Array.from(byModule.keys()).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
156
156
|
for (const module of sortedModules) {
|
|
157
157
|
const tools = byModule.get(module);
|
|
158
158
|
console.log(`${module} (${tools.length} tools):`);
|
|
159
|
-
for (const name of tools.sort()) {
|
|
159
|
+
for (const name of tools.sort((a, b) => a < b ? -1 : a > b ? 1 : 0)) {
|
|
160
160
|
const tool = registry.getTool(name);
|
|
161
161
|
if (!tool) continue;
|
|
162
162
|
if (verbose) {
|
|
@@ -256,7 +256,7 @@ const testTools = {
|
|
|
256
256
|
list.push(record);
|
|
257
257
|
byModule.set(record.module, list);
|
|
258
258
|
}
|
|
259
|
-
const sortedModules = Array.from(byModule.keys()).sort();
|
|
259
|
+
const sortedModules = Array.from(byModule.keys()).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
260
260
|
for (const moduleId of sortedModules) {
|
|
261
261
|
const list = byModule.get(moduleId);
|
|
262
262
|
console.log("");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/ai_assistant/cli.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\n/**\n * Ensure app bootstrap is called before creating DI container.\n * Uses the shared generated-bootstrap loader so the command works both from\n * the monorepo app and from standalone apps installed through npm packages.\n */\nasync function ensureBootstrap(): Promise<void> {\n // First check if DI is already available\n try {\n const { getDiRegistrars } = await import('@open-mercato/shared/lib/di/container')\n getDiRegistrars()\n return // DI already available\n } catch {\n // DI not available, need to bootstrap\n }\n\n try {\n const { bootstrapFromAppRoot } = await import('@open-mercato/shared/lib/bootstrap/dynamicLoader')\n await bootstrapFromAppRoot()\n } catch (error) {\n console.error('[MCP] Bootstrap failed:', error instanceof Error ? error.message : error)\n // Continue - some contexts may not have bootstrap available\n }\n}\n\nfunction parseArgs(rest: string[]): Record<string, string | boolean> {\n const args: Record<string, string | boolean> = {}\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i]\n if (!arg?.startsWith('--')) continue\n\n const [key, value] = arg.replace(/^--/, '').split('=')\n if (value !== undefined) {\n args[key] = value\n } else if (rest[i + 1] && !rest[i + 1]!.startsWith('--')) {\n args[key] = rest[i + 1]!\n i++\n } else {\n args[key] = true\n }\n }\n return args\n}\n\nconst mcpServe: ModuleCli = {\n command: 'mcp:serve',\n async run(rest) {\n const args = parseArgs(rest)\n // Prefer the OPEN_MERCATO_API_KEY env var so the secret never has to be\n // placed on the command line (argv is world-readable via ps / /proc).\n // The --api-key flag stays supported for backward compatibility.\n const apiKey = String(args['api-key'] ?? args.apiKey ?? '') || process.env.OPEN_MERCATO_API_KEY || null\n const tenantId = String(args.tenant ?? args.tenantId ?? '') || null\n const organizationId = String(args.org ?? args.organizationId ?? '') || null\n const userId = String(args.user ?? args.userId ?? '') || null\n const debug = args.debug === true || args.debug === 'true'\n const allowUnauthenticatedSuperadmin =\n args['allow-unauthenticated-superadmin'] === true ||\n args['allow-unauthenticated-superadmin'] === 'true'\n\n // Either API key or tenant is required\n if (!apiKey && !tenantId) {\n console.error('Usage: mercato ai_assistant mcp:serve [options]')\n console.error('')\n console.error('Authentication (choose one):')\n console.error(' OPEN_MERCATO_API_KEY env API key secret (recommended \u2014 keeps the secret off argv)')\n console.error(' --api-key <secret> API key secret for authentication (visible in process listings)')\n console.error(' --tenant <id> Tenant ID (for manual context)')\n console.error('')\n console.error('Options (with --tenant):')\n console.error(' --org <id> Organization ID (optional)')\n console.error(' --user <id> User ID for ACL (required unless --allow-unauthenticated-superadmin is set)')\n console.error('')\n console.error('Common options:')\n console.error(' --debug Enable debug logging')\n console.error(' --allow-unauthenticated-superadmin')\n console.error(' DEV/TEST ONLY: run as superadmin with no per-user ACL when')\n console.error(' no --user (and no --api-key) is supplied. Never use in production.')\n console.error('')\n console.error('Examples:')\n console.error(' OPEN_MERCATO_API_KEY=omk_xxxx.yyyy... mercato ai_assistant mcp:serve')\n console.error(' mercato ai_assistant mcp:serve --api-key omk_xxxx.yyyy...')\n console.error(' mercato ai_assistant mcp:serve --tenant 123e4567-e89b-12d3-a456-426614174000 --user <user-id>')\n console.error(' mercato ai_assistant mcp:serve --tenant 123e4567-e89b-12d3-a456-426614174000 --allow-unauthenticated-superadmin')\n return\n }\n\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runMcpServer } = await import('./lib/mcp-server')\n\n if (apiKey) {\n await runMcpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n apiKeySecret: apiKey,\n })\n } else {\n await runMcpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n context: {\n tenantId,\n organizationId,\n userId,\n },\n allowUnauthenticatedSuperadmin,\n })\n }\n },\n}\n\nconst MCP_DEFAULT_PORT = 3001\n\nconst mcpServeHttp: ModuleCli = {\n command: 'mcp:serve-http',\n async run(rest) {\n const args = parseArgs(rest)\n const portArg = parseInt(String(args.port ?? ''), 10)\n const port = !portArg || isNaN(portArg) ? MCP_DEFAULT_PORT : portArg\n const debug = args.debug === true || args.debug === 'true'\n\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runMcpHttpServer } = await import('./lib/http-server')\n\n await runMcpHttpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n port,\n })\n },\n}\n\nconst mcpDev: ModuleCli = {\n command: 'mcp:dev',\n async run() {\n await ensureBootstrap()\n const { runMcpDevServer } = await import('./lib/mcp-dev-server')\n await runMcpDevServer()\n },\n}\n\nconst listTools: ModuleCli = {\n command: 'mcp:list-tools',\n async run(rest) {\n const args = parseArgs(rest)\n const verbose = args.verbose === true || args.verbose === 'true'\n\n // Ensure bootstrap runs so modules are registered for API discovery\n await ensureBootstrap()\n\n const { loadAllModuleTools } = await import('./lib/tool-loader')\n await loadAllModuleTools()\n\n const { getToolRegistry } = await import('./lib/tool-registry')\n const registry = getToolRegistry()\n const toolNames = registry.listToolNames()\n\n if (toolNames.length === 0) {\n console.log('\\nNo MCP tools registered.')\n console.log('Tools can be registered by modules using registerMcpTool().\\n')\n return\n }\n\n console.log(`\\nRegistered MCP Tools (${toolNames.length}):\\n`)\n\n // Group tools by module\n const byModule = new Map<string, string[]>()\n for (const name of toolNames) {\n const [module] = name.split('.')\n const list = byModule.get(module) ?? []\n list.push(name)\n byModule.set(module, list)\n }\n\n // Sort modules alphabetically\n const sortedModules = Array.from(byModule.keys()).sort()\n\n for (const module of sortedModules) {\n const tools = byModule.get(module)!\n console.log(`${module} (${tools.length} tools):`)\n\n for (const name of tools.sort()) {\n const tool = registry.getTool(name)\n if (!tool) continue\n\n if (verbose) {\n console.log(` ${name}`)\n console.log(` ${tool.description}`)\n if (tool.requiredFeatures?.length) {\n console.log(` Requires: ${tool.requiredFeatures.join(', ')}`)\n }\n } else {\n console.log(` - ${name}`)\n }\n }\n console.log('')\n }\n },\n}\n\nconst entityGraph: ModuleCli = {\n command: 'entity-graph',\n async run(rest) {\n const args = parseArgs(rest)\n const format = String(args.format ?? 'triples') as 'json' | 'triples'\n const entity = args.entity ? String(args.entity) : undefined\n const module = args.module ? String(args.module) : undefined\n\n await ensureBootstrap()\n\n const { getOrm } = await import('@open-mercato/shared/lib/db/mikro')\n const { extractEntityGraph, formatGraphAsTriples, filterGraphByEntity, filterGraphByModule } = await import(\n './lib/entity-graph'\n )\n\n console.log('[Entity Graph] Extracting from MikroORM metadata...')\n\n const orm = await getOrm()\n const graph = await extractEntityGraph(orm)\n\n // Apply filters\n let edges = graph.edges\n\n if (entity) {\n edges = filterGraphByEntity(graph, entity)\n console.log(`[Entity Graph] Filtered by entity: ${entity}`)\n }\n\n if (module) {\n const filteredGraph = { ...graph, edges }\n edges = filterGraphByModule(filteredGraph, module)\n console.log(`[Entity Graph] Filtered by module: ${module}`)\n }\n\n const filteredGraph = { ...graph, edges }\n\n if (format === 'json') {\n console.log(JSON.stringify(filteredGraph, null, 2))\n } else {\n const triples = formatGraphAsTriples(filteredGraph)\n console.log('')\n for (const triple of triples) {\n console.log(triple)\n }\n }\n\n console.log(`\\n[Entity Graph] ${graph.nodes.length} entities, ${edges.length} relationships`)\n },\n}\n\nconst runPendingActionCleanup: ModuleCli = {\n command: 'run-pending-action-cleanup',\n async run() {\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runPendingActionCleanup: runCleanup } = await import(\n './workers/ai-pending-action-cleanup'\n )\n\n const em = container.resolve<import('@mikro-orm/postgresql').EntityManager>('em')\n const summary = await runCleanup({ em })\n\n console.log('[ai-pending-action-cleanup] Sweep complete:', summary)\n },\n}\n\nconst testTools: ModuleCli = {\n command: 'test-tools',\n async run(rest) {\n const args = parseArgs(rest)\n const json = args.json === true || args.json === 'true'\n const moduleFilter =\n typeof args.module === 'string' && args.module.length > 0 ? args.module : null\n const includeMutations = args['no-mutations'] !== true && args['no-mutations'] !== 'true'\n const tenantId =\n typeof args.tenant === 'string' && args.tenant.length > 0 ? args.tenant : null\n const organizationId =\n typeof args.org === 'string' && args.org.length > 0 ? args.org : null\n\n await ensureBootstrap()\n const { runToolTests } = await import('./lib/tool-test-runner')\n const report = await runToolTests({\n tenantId,\n organizationId,\n moduleFilter,\n includeMutations,\n })\n\n if (json) {\n // Wrap in markers so a Playwright spec (or any caller) can extract the\n // JSON payload without being thrown off by bootstrap log lines emitted\n // to stdout by other modules during DI container creation.\n console.log('---TOOL_TEST_REPORT_BEGIN---')\n console.log(JSON.stringify(report))\n console.log('---TOOL_TEST_REPORT_END---')\n } else {\n console.log('')\n console.log(\n `AI tool test report \u2014 tenant=${report.tenantId ?? '<none>'} org=${\n report.organizationId ?? '<none>'\n }`,\n )\n console.log(\n `total=${report.total} pass=${report.passed} fail=${report.failed} skip=${report.skipped}`,\n )\n const byModule = new Map<string, typeof report.records>()\n for (const record of report.records) {\n const list = byModule.get(record.module) ?? []\n list.push(record)\n byModule.set(record.module, list)\n }\n const sortedModules = Array.from(byModule.keys()).sort()\n for (const moduleId of sortedModules) {\n const list = byModule.get(moduleId)!\n console.log('')\n console.log(`${moduleId} (${list.length}):`)\n for (const record of list) {\n const marker =\n record.status === 'pass' ? '\u2713' : record.status === 'fail' ? '\u2717' : '\u00B7'\n const reason = record.reason ? ` \u2014 ${record.reason}` : ''\n const mutation = record.isMutation ? ' [mutation]' : ''\n console.log(\n ` ${marker} ${record.tool}${mutation} (${record.durationMs}ms)${reason}`,\n )\n }\n }\n console.log('')\n }\n\n if (report.failed > 0) {\n process.exitCode = 1\n }\n },\n}\n\nconst runTokenUsagePrune: ModuleCli = {\n command: 'run-token-usage-prune',\n async run() {\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runTokenUsagePrune: runPrune } = await import(\n './workers/ai-token-usage-prune'\n )\n\n const em = container.resolve<import('@mikro-orm/postgresql').EntityManager>('em')\n const summary = await runPrune({ em })\n\n console.log('[ai-token-usage-prune] Prune complete:', summary)\n },\n}\n\nexport default [\n mcpServe,\n mcpServeHttp,\n mcpDev,\n listTools,\n entityGraph,\n runPendingActionCleanup,\n runTokenUsagePrune,\n testTools,\n]\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\n/**\n * Ensure app bootstrap is called before creating DI container.\n * Uses the shared generated-bootstrap loader so the command works both from\n * the monorepo app and from standalone apps installed through npm packages.\n */\nasync function ensureBootstrap(): Promise<void> {\n // First check if DI is already available\n try {\n const { getDiRegistrars } = await import('@open-mercato/shared/lib/di/container')\n getDiRegistrars()\n return // DI already available\n } catch {\n // DI not available, need to bootstrap\n }\n\n try {\n const { bootstrapFromAppRoot } = await import('@open-mercato/shared/lib/bootstrap/dynamicLoader')\n await bootstrapFromAppRoot()\n } catch (error) {\n console.error('[MCP] Bootstrap failed:', error instanceof Error ? error.message : error)\n // Continue - some contexts may not have bootstrap available\n }\n}\n\nfunction parseArgs(rest: string[]): Record<string, string | boolean> {\n const args: Record<string, string | boolean> = {}\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i]\n if (!arg?.startsWith('--')) continue\n\n const [key, value] = arg.replace(/^--/, '').split('=')\n if (value !== undefined) {\n args[key] = value\n } else if (rest[i + 1] && !rest[i + 1]!.startsWith('--')) {\n args[key] = rest[i + 1]!\n i++\n } else {\n args[key] = true\n }\n }\n return args\n}\n\nconst mcpServe: ModuleCli = {\n command: 'mcp:serve',\n async run(rest) {\n const args = parseArgs(rest)\n // Prefer the OPEN_MERCATO_API_KEY env var so the secret never has to be\n // placed on the command line (argv is world-readable via ps / /proc).\n // The --api-key flag stays supported for backward compatibility.\n const apiKey = String(args['api-key'] ?? args.apiKey ?? '') || process.env.OPEN_MERCATO_API_KEY || null\n const tenantId = String(args.tenant ?? args.tenantId ?? '') || null\n const organizationId = String(args.org ?? args.organizationId ?? '') || null\n const userId = String(args.user ?? args.userId ?? '') || null\n const debug = args.debug === true || args.debug === 'true'\n const allowUnauthenticatedSuperadmin =\n args['allow-unauthenticated-superadmin'] === true ||\n args['allow-unauthenticated-superadmin'] === 'true'\n\n // Either API key or tenant is required\n if (!apiKey && !tenantId) {\n console.error('Usage: mercato ai_assistant mcp:serve [options]')\n console.error('')\n console.error('Authentication (choose one):')\n console.error(' OPEN_MERCATO_API_KEY env API key secret (recommended \u2014 keeps the secret off argv)')\n console.error(' --api-key <secret> API key secret for authentication (visible in process listings)')\n console.error(' --tenant <id> Tenant ID (for manual context)')\n console.error('')\n console.error('Options (with --tenant):')\n console.error(' --org <id> Organization ID (optional)')\n console.error(' --user <id> User ID for ACL (required unless --allow-unauthenticated-superadmin is set)')\n console.error('')\n console.error('Common options:')\n console.error(' --debug Enable debug logging')\n console.error(' --allow-unauthenticated-superadmin')\n console.error(' DEV/TEST ONLY: run as superadmin with no per-user ACL when')\n console.error(' no --user (and no --api-key) is supplied. Never use in production.')\n console.error('')\n console.error('Examples:')\n console.error(' OPEN_MERCATO_API_KEY=omk_xxxx.yyyy... mercato ai_assistant mcp:serve')\n console.error(' mercato ai_assistant mcp:serve --api-key omk_xxxx.yyyy...')\n console.error(' mercato ai_assistant mcp:serve --tenant 123e4567-e89b-12d3-a456-426614174000 --user <user-id>')\n console.error(' mercato ai_assistant mcp:serve --tenant 123e4567-e89b-12d3-a456-426614174000 --allow-unauthenticated-superadmin')\n return\n }\n\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runMcpServer } = await import('./lib/mcp-server')\n\n if (apiKey) {\n await runMcpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n apiKeySecret: apiKey,\n })\n } else {\n await runMcpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n context: {\n tenantId,\n organizationId,\n userId,\n },\n allowUnauthenticatedSuperadmin,\n })\n }\n },\n}\n\nconst MCP_DEFAULT_PORT = 3001\n\nconst mcpServeHttp: ModuleCli = {\n command: 'mcp:serve-http',\n async run(rest) {\n const args = parseArgs(rest)\n const portArg = parseInt(String(args.port ?? ''), 10)\n const port = !portArg || isNaN(portArg) ? MCP_DEFAULT_PORT : portArg\n const debug = args.debug === true || args.debug === 'true'\n\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runMcpHttpServer } = await import('./lib/http-server')\n\n await runMcpHttpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n port,\n })\n },\n}\n\nconst mcpDev: ModuleCli = {\n command: 'mcp:dev',\n async run() {\n await ensureBootstrap()\n const { runMcpDevServer } = await import('./lib/mcp-dev-server')\n await runMcpDevServer()\n },\n}\n\nconst listTools: ModuleCli = {\n command: 'mcp:list-tools',\n async run(rest) {\n const args = parseArgs(rest)\n const verbose = args.verbose === true || args.verbose === 'true'\n\n // Ensure bootstrap runs so modules are registered for API discovery\n await ensureBootstrap()\n\n const { loadAllModuleTools } = await import('./lib/tool-loader')\n await loadAllModuleTools()\n\n const { getToolRegistry } = await import('./lib/tool-registry')\n const registry = getToolRegistry()\n const toolNames = registry.listToolNames()\n\n if (toolNames.length === 0) {\n console.log('\\nNo MCP tools registered.')\n console.log('Tools can be registered by modules using registerMcpTool().\\n')\n return\n }\n\n console.log(`\\nRegistered MCP Tools (${toolNames.length}):\\n`)\n\n // Group tools by module\n const byModule = new Map<string, string[]>()\n for (const name of toolNames) {\n const [module] = name.split('.')\n const list = byModule.get(module) ?? []\n list.push(name)\n byModule.set(module, list)\n }\n\n // Sort modules alphabetically\n const sortedModules = Array.from(byModule.keys()).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))\n\n for (const module of sortedModules) {\n const tools = byModule.get(module)!\n console.log(`${module} (${tools.length} tools):`)\n\n for (const name of tools.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))) {\n const tool = registry.getTool(name)\n if (!tool) continue\n\n if (verbose) {\n console.log(` ${name}`)\n console.log(` ${tool.description}`)\n if (tool.requiredFeatures?.length) {\n console.log(` Requires: ${tool.requiredFeatures.join(', ')}`)\n }\n } else {\n console.log(` - ${name}`)\n }\n }\n console.log('')\n }\n },\n}\n\nconst entityGraph: ModuleCli = {\n command: 'entity-graph',\n async run(rest) {\n const args = parseArgs(rest)\n const format = String(args.format ?? 'triples') as 'json' | 'triples'\n const entity = args.entity ? String(args.entity) : undefined\n const module = args.module ? String(args.module) : undefined\n\n await ensureBootstrap()\n\n const { getOrm } = await import('@open-mercato/shared/lib/db/mikro')\n const { extractEntityGraph, formatGraphAsTriples, filterGraphByEntity, filterGraphByModule } = await import(\n './lib/entity-graph'\n )\n\n console.log('[Entity Graph] Extracting from MikroORM metadata...')\n\n const orm = await getOrm()\n const graph = await extractEntityGraph(orm)\n\n // Apply filters\n let edges = graph.edges\n\n if (entity) {\n edges = filterGraphByEntity(graph, entity)\n console.log(`[Entity Graph] Filtered by entity: ${entity}`)\n }\n\n if (module) {\n const filteredGraph = { ...graph, edges }\n edges = filterGraphByModule(filteredGraph, module)\n console.log(`[Entity Graph] Filtered by module: ${module}`)\n }\n\n const filteredGraph = { ...graph, edges }\n\n if (format === 'json') {\n console.log(JSON.stringify(filteredGraph, null, 2))\n } else {\n const triples = formatGraphAsTriples(filteredGraph)\n console.log('')\n for (const triple of triples) {\n console.log(triple)\n }\n }\n\n console.log(`\\n[Entity Graph] ${graph.nodes.length} entities, ${edges.length} relationships`)\n },\n}\n\nconst runPendingActionCleanup: ModuleCli = {\n command: 'run-pending-action-cleanup',\n async run() {\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runPendingActionCleanup: runCleanup } = await import(\n './workers/ai-pending-action-cleanup'\n )\n\n const em = container.resolve<import('@mikro-orm/postgresql').EntityManager>('em')\n const summary = await runCleanup({ em })\n\n console.log('[ai-pending-action-cleanup] Sweep complete:', summary)\n },\n}\n\nconst testTools: ModuleCli = {\n command: 'test-tools',\n async run(rest) {\n const args = parseArgs(rest)\n const json = args.json === true || args.json === 'true'\n const moduleFilter =\n typeof args.module === 'string' && args.module.length > 0 ? args.module : null\n const includeMutations = args['no-mutations'] !== true && args['no-mutations'] !== 'true'\n const tenantId =\n typeof args.tenant === 'string' && args.tenant.length > 0 ? args.tenant : null\n const organizationId =\n typeof args.org === 'string' && args.org.length > 0 ? args.org : null\n\n await ensureBootstrap()\n const { runToolTests } = await import('./lib/tool-test-runner')\n const report = await runToolTests({\n tenantId,\n organizationId,\n moduleFilter,\n includeMutations,\n })\n\n if (json) {\n // Wrap in markers so a Playwright spec (or any caller) can extract the\n // JSON payload without being thrown off by bootstrap log lines emitted\n // to stdout by other modules during DI container creation.\n console.log('---TOOL_TEST_REPORT_BEGIN---')\n console.log(JSON.stringify(report))\n console.log('---TOOL_TEST_REPORT_END---')\n } else {\n console.log('')\n console.log(\n `AI tool test report \u2014 tenant=${report.tenantId ?? '<none>'} org=${\n report.organizationId ?? '<none>'\n }`,\n )\n console.log(\n `total=${report.total} pass=${report.passed} fail=${report.failed} skip=${report.skipped}`,\n )\n const byModule = new Map<string, typeof report.records>()\n for (const record of report.records) {\n const list = byModule.get(record.module) ?? []\n list.push(record)\n byModule.set(record.module, list)\n }\n const sortedModules = Array.from(byModule.keys()).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))\n for (const moduleId of sortedModules) {\n const list = byModule.get(moduleId)!\n console.log('')\n console.log(`${moduleId} (${list.length}):`)\n for (const record of list) {\n const marker =\n record.status === 'pass' ? '\u2713' : record.status === 'fail' ? '\u2717' : '\u00B7'\n const reason = record.reason ? ` \u2014 ${record.reason}` : ''\n const mutation = record.isMutation ? ' [mutation]' : ''\n console.log(\n ` ${marker} ${record.tool}${mutation} (${record.durationMs}ms)${reason}`,\n )\n }\n }\n console.log('')\n }\n\n if (report.failed > 0) {\n process.exitCode = 1\n }\n },\n}\n\nconst runTokenUsagePrune: ModuleCli = {\n command: 'run-token-usage-prune',\n async run() {\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runTokenUsagePrune: runPrune } = await import(\n './workers/ai-token-usage-prune'\n )\n\n const em = container.resolve<import('@mikro-orm/postgresql').EntityManager>('em')\n const summary = await runPrune({ em })\n\n console.log('[ai-token-usage-prune] Prune complete:', summary)\n },\n}\n\nexport default [\n mcpServe,\n mcpServeHttp,\n mcpDev,\n listTools,\n entityGraph,\n runPendingActionCleanup,\n runTokenUsagePrune,\n testTools,\n]\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["filteredGraph"]
|
|
7
7
|
}
|
|
@@ -175,7 +175,7 @@ function getGraphStats(graph) {
|
|
|
175
175
|
return {
|
|
176
176
|
totalEntities: graph.nodes.length,
|
|
177
177
|
totalRelationships: graph.edges.length,
|
|
178
|
-
modules: Array.from(byModule.keys()).sort()
|
|
178
|
+
modules: Array.from(byModule.keys()).sort((a, b) => a < b ? -1 : a > b ? 1 : 0)
|
|
179
179
|
};
|
|
180
180
|
}
|
|
181
181
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/ai_assistant/lib/entity-graph.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Entity Relationship Graph Extraction\n *\n * Extracts entity relationships from MikroORM metadata and provides\n * them in a format suitable for AI tools to query.\n */\n\nimport { ReferenceKind, type MikroORM } from '@mikro-orm/core'\nimport type { PostgreSqlDriver } from '@mikro-orm/postgresql'\n\n/**\n * Relationship types mapped from MikroORM reference kinds.\n */\nexport type RelationshipType =\n | 'BELONGS_TO' // ManyToOne\n | 'HAS_MANY' // OneToMany\n | 'HAS_ONE' // OneToOne (owner)\n | 'BELONGS_TO_ONE' // OneToOne (inverse)\n | 'HAS_MANY_MANY' // ManyToMany (owner)\n | 'BELONGS_TO_MANY' // ManyToMany (inverse)\n\n/**\n * A relationship triple representing a connection between two entities.\n */\nexport interface EntityTriple {\n source: string\n relationship: RelationshipType\n target: string\n property: string\n nullable?: boolean\n}\n\n/**\n * An entity node with its properties.\n */\nexport interface EntityNode {\n className: string\n tableName: string\n properties: Array<{ name: string; type: string; nullable: boolean }>\n}\n\n/**\n * The complete entity graph with nodes and edges.\n */\nexport interface EntityGraph {\n nodes: EntityNode[]\n edges: EntityTriple[]\n generatedAt: string\n}\n\n/**\n * In-memory cache for the entity graph.\n */\nlet cachedGraph: EntityGraph | null = null\n\n/**\n * Map MikroORM ReferenceKind to our RelationshipType.\n */\nfunction mapReferenceKind(kind: ReferenceKind, mappedBy?: string): RelationshipType {\n switch (kind) {\n case ReferenceKind.MANY_TO_ONE:\n return 'BELONGS_TO'\n case ReferenceKind.ONE_TO_MANY:\n return 'HAS_MANY'\n case ReferenceKind.ONE_TO_ONE:\n // If mappedBy is set, this is the inverse side\n return mappedBy ? 'BELONGS_TO_ONE' : 'HAS_ONE'\n case ReferenceKind.MANY_TO_MANY:\n // If mappedBy is set, this is the inverse side\n return mappedBy ? 'BELONGS_TO_MANY' : 'HAS_MANY_MANY'\n default:\n return 'BELONGS_TO'\n }\n}\n\n/**\n * Get a simple type name from MikroORM property type.\n */\nfunction getSimpleTypeName(type: string | ((...args: unknown[]) => unknown) | undefined): string {\n if (!type) return 'unknown'\n if (typeof type === 'function') return type.name || 'unknown'\n return type\n}\n\n/**\n * Extract the entity graph from MikroORM metadata.\n */\nexport async function extractEntityGraph(orm: MikroORM<PostgreSqlDriver>): Promise<EntityGraph> {\n const metadata = orm.getMetadata()\n const allMetadata = metadata.getAll()\n\n const nodes: EntityNode[] = []\n const edges: EntityTriple[] = []\n\n for (const [, entityMeta] of Object.entries(allMetadata)) {\n // Skip abstract entities and embeddables\n if (entityMeta.abstract || entityMeta.embeddable) continue\n\n // Skip internal MikroORM entities\n if (entityMeta.className.startsWith('MikroORM')) continue\n\n const properties: Array<{ name: string; type: string; nullable: boolean }> = []\n\n for (const prop of entityMeta.props) {\n // Skip internal properties\n if (prop.name.startsWith('_')) continue\n\n // Handle relationships\n if (prop.kind !== undefined && prop.kind !== ReferenceKind.SCALAR) {\n // This is a relationship property\n const targetEntity = prop.type\n if (targetEntity && targetEntity !== entityMeta.className) {\n const relationship = mapReferenceKind(prop.kind, prop.mappedBy)\n\n edges.push({\n source: entityMeta.className,\n relationship,\n target: targetEntity,\n property: prop.name,\n nullable: prop.nullable ?? false,\n })\n }\n } else {\n // Regular scalar property\n properties.push({\n name: prop.name,\n type: getSimpleTypeName(prop.type),\n nullable: prop.nullable ?? false,\n })\n }\n }\n\n nodes.push({\n className: entityMeta.className,\n tableName: entityMeta.tableName,\n properties,\n })\n }\n\n // Sort for consistent output\n nodes.sort((a, b) => a.className.localeCompare(b.className))\n edges.sort((a, b) => {\n const sourceCompare = a.source.localeCompare(b.source)\n if (sourceCompare !== 0) return sourceCompare\n return a.property.localeCompare(b.property)\n })\n\n return {\n nodes,\n edges,\n generatedAt: new Date().toISOString(),\n }\n}\n\n/**\n * Format the entity graph as readable triples.\n *\n * Example output:\n * (CustomerEntity)-[HAS_MANY:deals]->(CustomerDeal)\n * (SalesOrder)-[BELONGS_TO:channel]->(SalesChannel)\n */\nexport function formatGraphAsTriples(graph: EntityGraph): string[] {\n return graph.edges.map((edge) => {\n const nullable = edge.nullable ? '?' : ''\n return `(${edge.source})-[${edge.relationship}${nullable}:${edge.property}]->(${edge.target})`\n })\n}\n\n/**\n * Cache the entity graph in memory.\n */\nexport function cacheEntityGraph(graph: EntityGraph): void {\n cachedGraph = graph\n}\n\n/**\n * Retrieve the cached entity graph.\n */\nexport function getCachedEntityGraph(): EntityGraph | null {\n return cachedGraph\n}\n\n/**\n * Filter graph edges by entity name (source or target).\n */\nexport function filterGraphByEntity(graph: EntityGraph, entityName: string): EntityTriple[] {\n const lowerEntity = entityName.toLowerCase()\n return graph.edges.filter(\n (edge) => edge.source.toLowerCase().includes(lowerEntity) || edge.target.toLowerCase().includes(lowerEntity)\n )\n}\n\n/**\n * Filter graph edges by module (inferred from table name prefix).\n */\nexport function filterGraphByModule(graph: EntityGraph, moduleName: string): EntityTriple[] {\n const lowerModule = moduleName.toLowerCase()\n\n // Find entities that belong to this module (by table name prefix or class name)\n const moduleEntities = new Set<string>()\n for (const node of graph.nodes) {\n if (node.tableName.startsWith(lowerModule) || node.className.toLowerCase().includes(lowerModule)) {\n moduleEntities.add(node.className)\n }\n }\n\n return graph.edges.filter((edge) => moduleEntities.has(edge.source) || moduleEntities.has(edge.target))\n}\n\n/**\n * Filter graph edges by relationship type.\n */\nexport function filterGraphByType(graph: EntityGraph, type: RelationshipType): EntityTriple[] {\n return graph.edges.filter((edge) => edge.relationship === type)\n}\n\n/**\n * Get entity fields for a specific entity.\n */\nexport function getEntityFields(graph: EntityGraph, entityName: string): EntityNode | undefined {\n const lowerEntity = entityName.toLowerCase()\n return graph.nodes.find((node) => node.className.toLowerCase() === lowerEntity)\n}\n\n/**\n * List all entities grouped by inferred module.\n */\nexport function listEntitiesByModule(graph: EntityGraph): Map<string, string[]> {\n const byModule = new Map<string, string[]>()\n\n for (const node of graph.nodes) {\n // Infer module from table name prefix (e.g., 'sales_orders' -> 'sales')\n const module = inferModuleFromEntity(node.className, node.tableName)\n\n const existing = byModule.get(module) ?? []\n existing.push(node.className)\n byModule.set(module, existing)\n }\n\n return byModule\n}\n\n/**\n * Infer module name from entity class name or table name.\n *\n * Patterns:\n * - Table prefix: 'sales_orders' \u2192 'sales'\n * - Class prefix: 'SalesOrder' \u2192 'sales' (PascalCase to module)\n * - Common mappings: CustomerEntity \u2192 'customers', CatalogProduct \u2192 'catalog'\n */\nexport function inferModuleFromEntity(className: string, tableName: string): string {\n // First try table name prefix (most reliable)\n const tableParts = tableName.split('_')\n if (tableParts.length > 1) {\n return tableParts[0]\n }\n\n // Try to extract from class name (e.g., SalesOrder \u2192 sales)\n // Handle common entity suffixes\n const nameWithoutSuffix = className\n .replace(/Entity$/, '')\n .replace(/Model$/, '')\n\n // Extract the first word from PascalCase\n const match = nameWithoutSuffix.match(/^([A-Z][a-z]+)/)\n if (match) {\n const prefix = match[1].toLowerCase()\n // Map common prefixes to module names\n const moduleMap: Record<string, string> = {\n sales: 'sales',\n customer: 'customers',\n catalog: 'catalog',\n product: 'catalog',\n order: 'sales',\n invoice: 'sales',\n quote: 'sales',\n auth: 'auth',\n user: 'auth',\n tenant: 'auth',\n organization: 'auth',\n workflow: 'workflows',\n config: 'configs',\n dictionary: 'dictionaries',\n entity: 'entities',\n search: 'search',\n attachment: 'attachments',\n audit: 'audit_logs',\n api: 'api_keys',\n dashboard: 'dashboards',\n widget: 'widgets',\n feature: 'feature_toggles',\n perspective: 'perspectives',\n currency: 'currencies',\n content: 'content',\n onboarding: 'onboarding',\n }\n if (moduleMap[prefix]) {\n return moduleMap[prefix]\n }\n return prefix\n }\n\n return 'core'\n}\n\n/**\n * Get both outgoing and incoming relationships for an entity.\n */\nexport function getEntityRelationships(\n graph: EntityGraph,\n entityName: string\n): { outgoing: EntityTriple[]; incoming: EntityTriple[] } {\n const outgoing = graph.edges.filter(\n (edge) => edge.source.toLowerCase() === entityName.toLowerCase()\n )\n const incoming = graph.edges.filter(\n (edge) =>\n edge.target.toLowerCase() === entityName.toLowerCase() &&\n edge.source.toLowerCase() !== entityName.toLowerCase()\n )\n return { outgoing, incoming }\n}\n\n/**\n * Format a single relationship triple as a string.\n */\nexport function formatTriple(edge: EntityTriple): string {\n const nullable = edge.nullable ? '?' : ''\n return `(${edge.source})-[${edge.relationship}${nullable}:${edge.property}]->(${edge.target})`\n}\n\n/**\n * Get graph statistics.\n */\nexport function getGraphStats(graph: EntityGraph): {\n totalEntities: number\n totalRelationships: number\n modules: string[]\n} {\n const byModule = listEntitiesByModule(graph)\n return {\n totalEntities: graph.nodes.length,\n totalRelationships: graph.edges.length,\n modules: Array.from(byModule.keys()).sort(),\n }\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/**\n * Entity Relationship Graph Extraction\n *\n * Extracts entity relationships from MikroORM metadata and provides\n * them in a format suitable for AI tools to query.\n */\n\nimport { ReferenceKind, type MikroORM } from '@mikro-orm/core'\nimport type { PostgreSqlDriver } from '@mikro-orm/postgresql'\n\n/**\n * Relationship types mapped from MikroORM reference kinds.\n */\nexport type RelationshipType =\n | 'BELONGS_TO' // ManyToOne\n | 'HAS_MANY' // OneToMany\n | 'HAS_ONE' // OneToOne (owner)\n | 'BELONGS_TO_ONE' // OneToOne (inverse)\n | 'HAS_MANY_MANY' // ManyToMany (owner)\n | 'BELONGS_TO_MANY' // ManyToMany (inverse)\n\n/**\n * A relationship triple representing a connection between two entities.\n */\nexport interface EntityTriple {\n source: string\n relationship: RelationshipType\n target: string\n property: string\n nullable?: boolean\n}\n\n/**\n * An entity node with its properties.\n */\nexport interface EntityNode {\n className: string\n tableName: string\n properties: Array<{ name: string; type: string; nullable: boolean }>\n}\n\n/**\n * The complete entity graph with nodes and edges.\n */\nexport interface EntityGraph {\n nodes: EntityNode[]\n edges: EntityTriple[]\n generatedAt: string\n}\n\n/**\n * In-memory cache for the entity graph.\n */\nlet cachedGraph: EntityGraph | null = null\n\n/**\n * Map MikroORM ReferenceKind to our RelationshipType.\n */\nfunction mapReferenceKind(kind: ReferenceKind, mappedBy?: string): RelationshipType {\n switch (kind) {\n case ReferenceKind.MANY_TO_ONE:\n return 'BELONGS_TO'\n case ReferenceKind.ONE_TO_MANY:\n return 'HAS_MANY'\n case ReferenceKind.ONE_TO_ONE:\n // If mappedBy is set, this is the inverse side\n return mappedBy ? 'BELONGS_TO_ONE' : 'HAS_ONE'\n case ReferenceKind.MANY_TO_MANY:\n // If mappedBy is set, this is the inverse side\n return mappedBy ? 'BELONGS_TO_MANY' : 'HAS_MANY_MANY'\n default:\n return 'BELONGS_TO'\n }\n}\n\n/**\n * Get a simple type name from MikroORM property type.\n */\nfunction getSimpleTypeName(type: string | ((...args: unknown[]) => unknown) | undefined): string {\n if (!type) return 'unknown'\n if (typeof type === 'function') return type.name || 'unknown'\n return type\n}\n\n/**\n * Extract the entity graph from MikroORM metadata.\n */\nexport async function extractEntityGraph(orm: MikroORM<PostgreSqlDriver>): Promise<EntityGraph> {\n const metadata = orm.getMetadata()\n const allMetadata = metadata.getAll()\n\n const nodes: EntityNode[] = []\n const edges: EntityTriple[] = []\n\n for (const [, entityMeta] of Object.entries(allMetadata)) {\n // Skip abstract entities and embeddables\n if (entityMeta.abstract || entityMeta.embeddable) continue\n\n // Skip internal MikroORM entities\n if (entityMeta.className.startsWith('MikroORM')) continue\n\n const properties: Array<{ name: string; type: string; nullable: boolean }> = []\n\n for (const prop of entityMeta.props) {\n // Skip internal properties\n if (prop.name.startsWith('_')) continue\n\n // Handle relationships\n if (prop.kind !== undefined && prop.kind !== ReferenceKind.SCALAR) {\n // This is a relationship property\n const targetEntity = prop.type\n if (targetEntity && targetEntity !== entityMeta.className) {\n const relationship = mapReferenceKind(prop.kind, prop.mappedBy)\n\n edges.push({\n source: entityMeta.className,\n relationship,\n target: targetEntity,\n property: prop.name,\n nullable: prop.nullable ?? false,\n })\n }\n } else {\n // Regular scalar property\n properties.push({\n name: prop.name,\n type: getSimpleTypeName(prop.type),\n nullable: prop.nullable ?? false,\n })\n }\n }\n\n nodes.push({\n className: entityMeta.className,\n tableName: entityMeta.tableName,\n properties,\n })\n }\n\n // Sort for consistent output\n nodes.sort((a, b) => a.className.localeCompare(b.className))\n edges.sort((a, b) => {\n const sourceCompare = a.source.localeCompare(b.source)\n if (sourceCompare !== 0) return sourceCompare\n return a.property.localeCompare(b.property)\n })\n\n return {\n nodes,\n edges,\n generatedAt: new Date().toISOString(),\n }\n}\n\n/**\n * Format the entity graph as readable triples.\n *\n * Example output:\n * (CustomerEntity)-[HAS_MANY:deals]->(CustomerDeal)\n * (SalesOrder)-[BELONGS_TO:channel]->(SalesChannel)\n */\nexport function formatGraphAsTriples(graph: EntityGraph): string[] {\n return graph.edges.map((edge) => {\n const nullable = edge.nullable ? '?' : ''\n return `(${edge.source})-[${edge.relationship}${nullable}:${edge.property}]->(${edge.target})`\n })\n}\n\n/**\n * Cache the entity graph in memory.\n */\nexport function cacheEntityGraph(graph: EntityGraph): void {\n cachedGraph = graph\n}\n\n/**\n * Retrieve the cached entity graph.\n */\nexport function getCachedEntityGraph(): EntityGraph | null {\n return cachedGraph\n}\n\n/**\n * Filter graph edges by entity name (source or target).\n */\nexport function filterGraphByEntity(graph: EntityGraph, entityName: string): EntityTriple[] {\n const lowerEntity = entityName.toLowerCase()\n return graph.edges.filter(\n (edge) => edge.source.toLowerCase().includes(lowerEntity) || edge.target.toLowerCase().includes(lowerEntity)\n )\n}\n\n/**\n * Filter graph edges by module (inferred from table name prefix).\n */\nexport function filterGraphByModule(graph: EntityGraph, moduleName: string): EntityTriple[] {\n const lowerModule = moduleName.toLowerCase()\n\n // Find entities that belong to this module (by table name prefix or class name)\n const moduleEntities = new Set<string>()\n for (const node of graph.nodes) {\n if (node.tableName.startsWith(lowerModule) || node.className.toLowerCase().includes(lowerModule)) {\n moduleEntities.add(node.className)\n }\n }\n\n return graph.edges.filter((edge) => moduleEntities.has(edge.source) || moduleEntities.has(edge.target))\n}\n\n/**\n * Filter graph edges by relationship type.\n */\nexport function filterGraphByType(graph: EntityGraph, type: RelationshipType): EntityTriple[] {\n return graph.edges.filter((edge) => edge.relationship === type)\n}\n\n/**\n * Get entity fields for a specific entity.\n */\nexport function getEntityFields(graph: EntityGraph, entityName: string): EntityNode | undefined {\n const lowerEntity = entityName.toLowerCase()\n return graph.nodes.find((node) => node.className.toLowerCase() === lowerEntity)\n}\n\n/**\n * List all entities grouped by inferred module.\n */\nexport function listEntitiesByModule(graph: EntityGraph): Map<string, string[]> {\n const byModule = new Map<string, string[]>()\n\n for (const node of graph.nodes) {\n // Infer module from table name prefix (e.g., 'sales_orders' -> 'sales')\n const module = inferModuleFromEntity(node.className, node.tableName)\n\n const existing = byModule.get(module) ?? []\n existing.push(node.className)\n byModule.set(module, existing)\n }\n\n return byModule\n}\n\n/**\n * Infer module name from entity class name or table name.\n *\n * Patterns:\n * - Table prefix: 'sales_orders' \u2192 'sales'\n * - Class prefix: 'SalesOrder' \u2192 'sales' (PascalCase to module)\n * - Common mappings: CustomerEntity \u2192 'customers', CatalogProduct \u2192 'catalog'\n */\nexport function inferModuleFromEntity(className: string, tableName: string): string {\n // First try table name prefix (most reliable)\n const tableParts = tableName.split('_')\n if (tableParts.length > 1) {\n return tableParts[0]\n }\n\n // Try to extract from class name (e.g., SalesOrder \u2192 sales)\n // Handle common entity suffixes\n const nameWithoutSuffix = className\n .replace(/Entity$/, '')\n .replace(/Model$/, '')\n\n // Extract the first word from PascalCase\n const match = nameWithoutSuffix.match(/^([A-Z][a-z]+)/)\n if (match) {\n const prefix = match[1].toLowerCase()\n // Map common prefixes to module names\n const moduleMap: Record<string, string> = {\n sales: 'sales',\n customer: 'customers',\n catalog: 'catalog',\n product: 'catalog',\n order: 'sales',\n invoice: 'sales',\n quote: 'sales',\n auth: 'auth',\n user: 'auth',\n tenant: 'auth',\n organization: 'auth',\n workflow: 'workflows',\n config: 'configs',\n dictionary: 'dictionaries',\n entity: 'entities',\n search: 'search',\n attachment: 'attachments',\n audit: 'audit_logs',\n api: 'api_keys',\n dashboard: 'dashboards',\n widget: 'widgets',\n feature: 'feature_toggles',\n perspective: 'perspectives',\n currency: 'currencies',\n content: 'content',\n onboarding: 'onboarding',\n }\n if (moduleMap[prefix]) {\n return moduleMap[prefix]\n }\n return prefix\n }\n\n return 'core'\n}\n\n/**\n * Get both outgoing and incoming relationships for an entity.\n */\nexport function getEntityRelationships(\n graph: EntityGraph,\n entityName: string\n): { outgoing: EntityTriple[]; incoming: EntityTriple[] } {\n const outgoing = graph.edges.filter(\n (edge) => edge.source.toLowerCase() === entityName.toLowerCase()\n )\n const incoming = graph.edges.filter(\n (edge) =>\n edge.target.toLowerCase() === entityName.toLowerCase() &&\n edge.source.toLowerCase() !== entityName.toLowerCase()\n )\n return { outgoing, incoming }\n}\n\n/**\n * Format a single relationship triple as a string.\n */\nexport function formatTriple(edge: EntityTriple): string {\n const nullable = edge.nullable ? '?' : ''\n return `(${edge.source})-[${edge.relationship}${nullable}:${edge.property}]->(${edge.target})`\n}\n\n/**\n * Get graph statistics.\n */\nexport function getGraphStats(graph: EntityGraph): {\n totalEntities: number\n totalRelationships: number\n modules: string[]\n} {\n const byModule = listEntitiesByModule(graph)\n return {\n totalEntities: graph.nodes.length,\n totalRelationships: graph.edges.length,\n modules: Array.from(byModule.keys()).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)),\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -120,7 +120,7 @@ function entityToIndexableRecord(entity) {
|
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
122
|
function computeEntitiesChecksum(entities) {
|
|
123
|
-
const content = entities.map((e) => `${e.className}:${e.tableName}:${e.fieldCount}`).sort().join("|");
|
|
123
|
+
const content = entities.map((e) => `${e.className}:${e.tableName}:${e.fieldCount}`).sort((a, b) => a < b ? -1 : a > b ? 1 : 0).join("|");
|
|
124
124
|
let hash = 0;
|
|
125
125
|
for (let i = 0; i < content.length; i++) {
|
|
126
126
|
const char = content.charCodeAt(i);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/ai_assistant/lib/entity-index-config.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Entity Schema Index Configuration\n *\n * Defines how database entity schemas are indexed in Meilisearch\n * for discovery via the discover_schema MCP tool.\n */\n\nimport type {\n SearchEntityConfig,\n SearchResultPresenter,\n IndexableRecord,\n} from '@open-mercato/search/types'\nimport type { EntityNode, EntityTriple } from './entity-graph'\n\n/**\n * Entity ID for entity schemas in the search index.\n * Following the module:entity naming convention.\n */\nexport const ENTITY_SCHEMA_ENTITY_ID = 'ai_assistant:entity_schema' as const\n\n/**\n * Tenant ID for global entity schemas.\n * Entity schemas are not tenant-scoped, so we use a special \"system\" UUID.\n * This is the nil UUID (all zeros) reserved for system-wide resources.\n */\nexport const GLOBAL_TENANT_ID = '00000000-0000-0000-0000-000000000000'\n\n/**\n * Default configuration for entity schema search.\n */\nexport const ENTITY_SCHEMA_SEARCH_CONFIG = {\n /** Maximum entities to return from search */\n defaultLimit: 10,\n /** Minimum relevance score (0-1) */\n minScore: 0.15,\n /** Strategies to use (in priority order) */\n strategies: ['fulltext', 'vector'] as const,\n} as const\n\n/**\n * Indexed entity structure with full schema information.\n */\nexport interface IndexedEntity {\n className: string\n tableName: string\n module: string\n fields: Array<{ name: string; type: string; nullable: boolean }>\n relationships: Array<{ relationship: string; target: string; property: string; nullable?: boolean }>\n}\n\n/**\n * Search entity configuration for entity schemas.\n * This configures how entities are indexed and searched.\n */\nexport const entitySchemaEntityConfig: SearchEntityConfig = {\n entityId: ENTITY_SCHEMA_ENTITY_ID,\n enabled: true,\n priority: 95, // High priority, above API endpoints\n\n /**\n * Build searchable content from an entity schema.\n */\n buildSource: (ctx) => {\n const entity = ctx.record as unknown as IndexedEntity\n const className = entity.className || ''\n const tableName = entity.tableName || ''\n const module = entity.module || ''\n const fields = entity.fields || []\n const relationships = entity.relationships || []\n\n // Build text content for embedding and fulltext search\n const textParts = [\n className,\n tableName.replace(/_/g, ' '),\n module,\n // Include field names for searchability\n ...fields.map((f) => f.name),\n // Include relationship targets for searchability\n ...relationships.map((r) => r.target),\n ]\n\n return {\n text: textParts.filter(Boolean).join(' | '),\n fields: {\n className,\n tableName,\n module,\n fieldCount: fields.length,\n relationshipCount: relationships.length,\n // Store full schema as JSON for retrieval\n schema: JSON.stringify({\n fields,\n relationships,\n }),\n },\n presenter: {\n title: className,\n subtitle: `${module} \u2022 ${fields.length} fields`,\n icon: 'lucide:database',\n },\n checksumSource: { className, tableName, fieldCount: fields.length },\n }\n },\n\n /**\n * Format result for display in search UI.\n */\n formatResult: (ctx) => {\n const entity = ctx.record as unknown as IndexedEntity\n return {\n title: entity.className,\n subtitle: `${entity.module} \u2022 ${entity.fields?.length ?? 0} fields`,\n icon: 'lucide:database',\n }\n },\n\n /**\n * Field policy for search strategies.\n */\n fieldPolicy: {\n searchable: ['className', 'tableName', 'module'],\n hashOnly: [],\n excluded: ['schema'], // Don't index the full JSON schema\n },\n}\n\n/**\n * Build search text from entity node and relationships.\n */\nfunction buildSearchText(entity: IndexedEntity): string {\n const parts = [\n entity.className,\n entity.tableName.replace(/_/g, ' '),\n entity.module,\n // Include all field names\n ...entity.fields.map((f) => f.name),\n // Include relationship targets\n ...entity.relationships.map((r) => `${r.relationship} ${r.target}`),\n ]\n return parts.filter(Boolean).join(' | ')\n}\n\n/**\n * Convert an entity schema to an indexable record for search.\n *\n * @param entity - The entity with full schema info\n * @returns IndexableRecord ready for search indexing\n */\nexport function entityToIndexableRecord(entity: IndexedEntity): IndexableRecord {\n const presenter: SearchResultPresenter = {\n title: entity.className,\n subtitle: `${entity.module} \u2022 ${entity.fields.length} fields`,\n icon: 'lucide:database',\n }\n\n return {\n entityId: ENTITY_SCHEMA_ENTITY_ID,\n recordId: entity.className,\n tenantId: GLOBAL_TENANT_ID,\n organizationId: null,\n fields: {\n className: entity.className,\n tableName: entity.tableName,\n module: entity.module,\n fieldCount: entity.fields.length,\n relationshipCount: entity.relationships.length,\n // Store full schema as JSON for retrieval\n schema: JSON.stringify({\n fields: entity.fields,\n relationships: entity.relationships,\n }),\n },\n presenter,\n text: buildSearchText(entity),\n checksumSource: {\n className: entity.className,\n tableName: entity.tableName,\n fieldCount: entity.fields.length,\n },\n }\n}\n\n/**\n * Compute a simple checksum for entity definitions.\n * Used to detect changes and avoid unnecessary re-indexing.\n */\nexport function computeEntitiesChecksum(\n entities: Array<{ className: string; tableName: string; fieldCount: number }>\n): string {\n const content = entities\n .map((e) => `${e.className}:${e.tableName}:${e.fieldCount}`)\n .sort()\n .join('|')\n\n // Simple hash using string code points\n let hash = 0\n for (let i = 0; i < content.length; i++) {\n const char = content.charCodeAt(i)\n hash = ((hash << 5) - hash + char) | 0\n }\n return hash.toString(16)\n}\n"],
|
|
5
|
-
"mappings": "AAkBO,MAAM,0BAA0B;AAOhC,MAAM,mBAAmB;AAKzB,MAAM,8BAA8B;AAAA;AAAA,EAEzC,cAAc;AAAA;AAAA,EAEd,UAAU;AAAA;AAAA,EAEV,YAAY,CAAC,YAAY,QAAQ;AACnC;AAiBO,MAAM,2BAA+C;AAAA,EAC1D,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,EAKV,aAAa,CAAC,QAAQ;AACpB,UAAM,SAAS,IAAI;AACnB,UAAM,YAAY,OAAO,aAAa;AACtC,UAAM,YAAY,OAAO,aAAa;AACtC,UAAM,SAAS,OAAO,UAAU;AAChC,UAAM,SAAS,OAAO,UAAU,CAAC;AACjC,UAAM,gBAAgB,OAAO,iBAAiB,CAAC;AAG/C,UAAM,YAAY;AAAA,MAChB;AAAA,MACA,UAAU,QAAQ,MAAM,GAAG;AAAA,MAC3B;AAAA;AAAA,MAEA,GAAG,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA;AAAA,MAE3B,GAAG,cAAc,IAAI,CAAC,MAAM,EAAE,MAAM;AAAA,IACtC;AAEA,WAAO;AAAA,MACL,MAAM,UAAU,OAAO,OAAO,EAAE,KAAK,KAAK;AAAA,MAC1C,QAAQ;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA,YAAY,OAAO;AAAA,QACnB,mBAAmB,cAAc;AAAA;AAAA,QAEjC,QAAQ,KAAK,UAAU;AAAA,UACrB;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAAA,MACA,WAAW;AAAA,QACT,OAAO;AAAA,QACP,UAAU,GAAG,MAAM,WAAM,OAAO,MAAM;AAAA,QACtC,MAAM;AAAA,MACR;AAAA,MACA,gBAAgB,EAAE,WAAW,WAAW,YAAY,OAAO,OAAO;AAAA,IACpE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,CAAC,QAAQ;AACrB,UAAM,SAAS,IAAI;AACnB,WAAO;AAAA,MACL,OAAO,OAAO;AAAA,MACd,UAAU,GAAG,OAAO,MAAM,WAAM,OAAO,QAAQ,UAAU,CAAC;AAAA,MAC1D,MAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa;AAAA,IACX,YAAY,CAAC,aAAa,aAAa,QAAQ;AAAA,IAC/C,UAAU,CAAC;AAAA,IACX,UAAU,CAAC,QAAQ;AAAA;AAAA,EACrB;AACF;AAKA,SAAS,gBAAgB,QAA+B;AACtD,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,IACP,OAAO,UAAU,QAAQ,MAAM,GAAG;AAAA,IAClC,OAAO;AAAA;AAAA,IAEP,GAAG,OAAO,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA;AAAA,IAElC,GAAG,OAAO,cAAc,IAAI,CAAC,MAAM,GAAG,EAAE,YAAY,IAAI,EAAE,MAAM,EAAE;AAAA,EACpE;AACA,SAAO,MAAM,OAAO,OAAO,EAAE,KAAK,KAAK;AACzC;AAQO,SAAS,wBAAwB,QAAwC;AAC9E,QAAM,YAAmC;AAAA,IACvC,OAAO,OAAO;AAAA,IACd,UAAU,GAAG,OAAO,MAAM,WAAM,OAAO,OAAO,MAAM;AAAA,IACpD,MAAM;AAAA,EACR;AAEA,SAAO;AAAA,IACL,UAAU;AAAA,IACV,UAAU,OAAO;AAAA,IACjB,UAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,QAAQ;AAAA,MACN,WAAW,OAAO;AAAA,MAClB,WAAW,OAAO;AAAA,MAClB,QAAQ,OAAO;AAAA,MACf,YAAY,OAAO,OAAO;AAAA,MAC1B,mBAAmB,OAAO,cAAc;AAAA;AAAA,MAExC,QAAQ,KAAK,UAAU;AAAA,QACrB,QAAQ,OAAO;AAAA,QACf,eAAe,OAAO;AAAA,MACxB,CAAC;AAAA,IACH;AAAA,IACA;AAAA,IACA,MAAM,gBAAgB,MAAM;AAAA,IAC5B,gBAAgB;AAAA,MACd,WAAW,OAAO;AAAA,MAClB,WAAW,OAAO;AAAA,MAClB,YAAY,OAAO,OAAO;AAAA,IAC5B;AAAA,EACF;AACF;AAMO,SAAS,wBACd,UACQ;AACR,QAAM,UAAU,SACb,IAAI,CAAC,MAAM,GAAG,EAAE,SAAS,IAAI,EAAE,SAAS,IAAI,EAAE,UAAU,EAAE,EAC1D,KAAK,
|
|
4
|
+
"sourcesContent": ["/**\n * Entity Schema Index Configuration\n *\n * Defines how database entity schemas are indexed in Meilisearch\n * for discovery via the discover_schema MCP tool.\n */\n\nimport type {\n SearchEntityConfig,\n SearchResultPresenter,\n IndexableRecord,\n} from '@open-mercato/search/types'\nimport type { EntityNode, EntityTriple } from './entity-graph'\n\n/**\n * Entity ID for entity schemas in the search index.\n * Following the module:entity naming convention.\n */\nexport const ENTITY_SCHEMA_ENTITY_ID = 'ai_assistant:entity_schema' as const\n\n/**\n * Tenant ID for global entity schemas.\n * Entity schemas are not tenant-scoped, so we use a special \"system\" UUID.\n * This is the nil UUID (all zeros) reserved for system-wide resources.\n */\nexport const GLOBAL_TENANT_ID = '00000000-0000-0000-0000-000000000000'\n\n/**\n * Default configuration for entity schema search.\n */\nexport const ENTITY_SCHEMA_SEARCH_CONFIG = {\n /** Maximum entities to return from search */\n defaultLimit: 10,\n /** Minimum relevance score (0-1) */\n minScore: 0.15,\n /** Strategies to use (in priority order) */\n strategies: ['fulltext', 'vector'] as const,\n} as const\n\n/**\n * Indexed entity structure with full schema information.\n */\nexport interface IndexedEntity {\n className: string\n tableName: string\n module: string\n fields: Array<{ name: string; type: string; nullable: boolean }>\n relationships: Array<{ relationship: string; target: string; property: string; nullable?: boolean }>\n}\n\n/**\n * Search entity configuration for entity schemas.\n * This configures how entities are indexed and searched.\n */\nexport const entitySchemaEntityConfig: SearchEntityConfig = {\n entityId: ENTITY_SCHEMA_ENTITY_ID,\n enabled: true,\n priority: 95, // High priority, above API endpoints\n\n /**\n * Build searchable content from an entity schema.\n */\n buildSource: (ctx) => {\n const entity = ctx.record as unknown as IndexedEntity\n const className = entity.className || ''\n const tableName = entity.tableName || ''\n const module = entity.module || ''\n const fields = entity.fields || []\n const relationships = entity.relationships || []\n\n // Build text content for embedding and fulltext search\n const textParts = [\n className,\n tableName.replace(/_/g, ' '),\n module,\n // Include field names for searchability\n ...fields.map((f) => f.name),\n // Include relationship targets for searchability\n ...relationships.map((r) => r.target),\n ]\n\n return {\n text: textParts.filter(Boolean).join(' | '),\n fields: {\n className,\n tableName,\n module,\n fieldCount: fields.length,\n relationshipCount: relationships.length,\n // Store full schema as JSON for retrieval\n schema: JSON.stringify({\n fields,\n relationships,\n }),\n },\n presenter: {\n title: className,\n subtitle: `${module} \u2022 ${fields.length} fields`,\n icon: 'lucide:database',\n },\n checksumSource: { className, tableName, fieldCount: fields.length },\n }\n },\n\n /**\n * Format result for display in search UI.\n */\n formatResult: (ctx) => {\n const entity = ctx.record as unknown as IndexedEntity\n return {\n title: entity.className,\n subtitle: `${entity.module} \u2022 ${entity.fields?.length ?? 0} fields`,\n icon: 'lucide:database',\n }\n },\n\n /**\n * Field policy for search strategies.\n */\n fieldPolicy: {\n searchable: ['className', 'tableName', 'module'],\n hashOnly: [],\n excluded: ['schema'], // Don't index the full JSON schema\n },\n}\n\n/**\n * Build search text from entity node and relationships.\n */\nfunction buildSearchText(entity: IndexedEntity): string {\n const parts = [\n entity.className,\n entity.tableName.replace(/_/g, ' '),\n entity.module,\n // Include all field names\n ...entity.fields.map((f) => f.name),\n // Include relationship targets\n ...entity.relationships.map((r) => `${r.relationship} ${r.target}`),\n ]\n return parts.filter(Boolean).join(' | ')\n}\n\n/**\n * Convert an entity schema to an indexable record for search.\n *\n * @param entity - The entity with full schema info\n * @returns IndexableRecord ready for search indexing\n */\nexport function entityToIndexableRecord(entity: IndexedEntity): IndexableRecord {\n const presenter: SearchResultPresenter = {\n title: entity.className,\n subtitle: `${entity.module} \u2022 ${entity.fields.length} fields`,\n icon: 'lucide:database',\n }\n\n return {\n entityId: ENTITY_SCHEMA_ENTITY_ID,\n recordId: entity.className,\n tenantId: GLOBAL_TENANT_ID,\n organizationId: null,\n fields: {\n className: entity.className,\n tableName: entity.tableName,\n module: entity.module,\n fieldCount: entity.fields.length,\n relationshipCount: entity.relationships.length,\n // Store full schema as JSON for retrieval\n schema: JSON.stringify({\n fields: entity.fields,\n relationships: entity.relationships,\n }),\n },\n presenter,\n text: buildSearchText(entity),\n checksumSource: {\n className: entity.className,\n tableName: entity.tableName,\n fieldCount: entity.fields.length,\n },\n }\n}\n\n/**\n * Compute a simple checksum for entity definitions.\n * Used to detect changes and avoid unnecessary re-indexing.\n */\nexport function computeEntitiesChecksum(\n entities: Array<{ className: string; tableName: string; fieldCount: number }>\n): string {\n const content = entities\n .map((e) => `${e.className}:${e.tableName}:${e.fieldCount}`)\n .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))\n .join('|')\n\n // Simple hash using string code points\n let hash = 0\n for (let i = 0; i < content.length; i++) {\n const char = content.charCodeAt(i)\n hash = ((hash << 5) - hash + char) | 0\n }\n return hash.toString(16)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -207,6 +207,21 @@ const OPENROUTER_PRESET = {
|
|
|
207
207
|
}
|
|
208
208
|
]
|
|
209
209
|
};
|
|
210
|
+
const REQUESTY_PRESET = {
|
|
211
|
+
id: "requesty",
|
|
212
|
+
name: "Requesty",
|
|
213
|
+
baseURL: "https://router.requesty.ai/v1",
|
|
214
|
+
baseURLEnvKeys: ["REQUESTY_BASE_URL"],
|
|
215
|
+
envKeys: ["REQUESTY_API_KEY"],
|
|
216
|
+
defaultModel: "openai/gpt-4o-mini",
|
|
217
|
+
defaultModels: [
|
|
218
|
+
{
|
|
219
|
+
id: "openai/gpt-4o-mini",
|
|
220
|
+
name: "GPT-4o mini",
|
|
221
|
+
contextWindow: 128e3
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
};
|
|
210
225
|
const LM_STUDIO_PRESET = {
|
|
211
226
|
id: "lm-studio",
|
|
212
227
|
name: "LM Studio (local)",
|
|
@@ -226,6 +241,7 @@ const OPENAI_COMPATIBLE_PRESETS = [
|
|
|
226
241
|
LITELLM_PRESET,
|
|
227
242
|
OLLAMA_PRESET,
|
|
228
243
|
OPENROUTER_PRESET,
|
|
244
|
+
REQUESTY_PRESET,
|
|
229
245
|
LM_STUDIO_PRESET
|
|
230
246
|
];
|
|
231
247
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/ai_assistant/lib/openai-compatible-presets.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Curated registry of OpenAI-compatible LLM backends.\n *\n * Each preset is plain data \u2014 adding a new backend takes one entry in\n * this array, zero new adapter files, and zero changes to route handlers.\n * The {@link createOpenAICompatibleProvider} factory in `./llm-adapters/openai.ts`\n * turns each preset into a concrete `LlmProvider` at bootstrap time.\n *\n * Preset model catalogs are curated snapshots as of 2026-04-14 and should\n * be updated as upstream catalogs evolve. Users can always override the\n * selected model via the `OPENCODE_MODEL` env var without editing this\n * file.\n *\n * @see ./llm-adapters/openai.ts\n * @see .ai/specs/implemented/2026-04-14-llm-provider-ports-and-adapters.md\n */\n\nimport type { OpenAICompatiblePreset } from './llm-adapters/openai'\n\n/**\n * Standard OpenAI \u2014 default OpenAI API at api.openai.com.\n */\nconst OPENAI_PRESET: OpenAICompatiblePreset = {\n id: 'openai',\n name: 'OpenAI',\n baseURL: undefined,\n baseURLEnvKeys: ['OPENAI_BASE_URL'],\n envKeys: ['OPENAI_API_KEY', 'OPENCODE_OPENAI_API_KEY'],\n defaultModel: 'gpt-5-mini',\n defaultModels: [\n {\n id: 'gpt-5-mini',\n name: 'GPT-5 Mini',\n contextWindow: 128000,\n tags: ['budget'],\n },\n {\n id: 'gpt-5',\n name: 'GPT-5',\n contextWindow: 128000,\n tags: ['flagship'],\n },\n {\n id: 'gpt-4o-mini',\n name: 'GPT-4o Mini',\n contextWindow: 128000,\n tags: ['budget'],\n },\n {\n id: 'gpt-4o',\n name: 'GPT-4o',\n contextWindow: 128000,\n },\n ],\n}\n\n/**\n * DeepInfra \u2014 hosts open-weight flagship models at 3-12\u00D7 lower cost than\n * the native APIs. The curated catalog targets the AI Assistant use case\n * (routing + tool use + conversational chat).\n */\nconst DEEPINFRA_PRESET: OpenAICompatiblePreset = {\n id: 'deepinfra',\n name: 'DeepInfra',\n baseURL: 'https://api.deepinfra.com/v1/openai',\n baseURLEnvKeys: ['DEEPINFRA_BASE_URL'],\n envKeys: ['DEEPINFRA_API_KEY'],\n defaultModel: 'zai-org/GLM-5.1',\n defaultModels: [\n {\n id: 'zai-org/GLM-5.1',\n name: 'GLM-5.1 (Zhipu)',\n contextWindow: 202752,\n tags: ['flagship'],\n },\n {\n id: 'zai-org/GLM-4.7-Flash',\n name: 'GLM-4.7 Flash',\n contextWindow: 202752,\n tags: ['budget'],\n },\n {\n id: 'Qwen/Qwen3-235B-A22B-Instruct-2507',\n name: 'Qwen3 235B (MoE)',\n contextWindow: 262144,\n tags: ['flagship'],\n },\n {\n id: 'meta-llama/Llama-4-Scout-17B-16E-Instruct',\n name: 'Llama 4 Scout',\n contextWindow: 327680,\n },\n {\n id: 'deepseek-ai/DeepSeek-V3.2-Exp',\n name: 'DeepSeek V3.2',\n contextWindow: 163840,\n tags: ['reasoning'],\n },\n {\n id: 'Qwen/Qwen3-Coder-30B-A3B-Instruct',\n name: 'Qwen3 Coder 30B',\n contextWindow: 262144,\n tags: ['coding'],\n },\n ],\n}\n\n/**\n * Groq \u2014 specializes in low-latency inference on LPU hardware.\n * Best suited for snappy tool-use and routing, less so for long reasoning.\n */\nconst GROQ_PRESET: OpenAICompatiblePreset = {\n id: 'groq',\n name: 'Groq',\n baseURL: 'https://api.groq.com/openai/v1',\n baseURLEnvKeys: ['GROQ_BASE_URL'],\n envKeys: ['GROQ_API_KEY'],\n defaultModel: 'llama-3.3-70b-versatile',\n defaultModels: [\n {\n id: 'llama-3.3-70b-versatile',\n name: 'Llama 3.3 70B Versatile',\n contextWindow: 131072,\n },\n {\n id: 'llama-4-scout-17b',\n name: 'Llama 4 Scout 17B',\n contextWindow: 131072,\n },\n {\n id: 'mixtral-8x22b-32768',\n name: 'Mixtral 8x22B',\n contextWindow: 32768,\n },\n ],\n}\n\n/**\n * Together AI \u2014 broad catalog of open-weight models with per-model pricing.\n */\nconst TOGETHER_PRESET: OpenAICompatiblePreset = {\n id: 'together',\n name: 'Together AI',\n baseURL: 'https://api.together.xyz/v1',\n baseURLEnvKeys: ['TOGETHER_BASE_URL'],\n envKeys: ['TOGETHER_API_KEY'],\n defaultModel: 'meta-llama/Llama-3.3-70B-Instruct-Turbo',\n defaultModels: [\n {\n id: 'meta-llama/Llama-3.3-70B-Instruct-Turbo',\n name: 'Llama 3.3 70B Turbo',\n contextWindow: 131072,\n },\n {\n id: 'Qwen/Qwen2.5-72B-Instruct-Turbo',\n name: 'Qwen 2.5 72B Turbo',\n contextWindow: 32768,\n },\n ],\n}\n\n/**\n * Fireworks AI \u2014 fast inference with a curated catalog.\n */\nconst FIREWORKS_PRESET: OpenAICompatiblePreset = {\n id: 'fireworks',\n name: 'Fireworks AI',\n baseURL: 'https://api.fireworks.ai/inference/v1',\n baseURLEnvKeys: ['FIREWORKS_BASE_URL'],\n envKeys: ['FIREWORKS_API_KEY'],\n defaultModel: 'accounts/fireworks/models/llama-v3p3-70b-instruct',\n defaultModels: [\n {\n id: 'accounts/fireworks/models/llama-v3p3-70b-instruct',\n name: 'Llama 3.3 70B',\n contextWindow: 131072,\n },\n ],\n}\n\n/**\n * Azure OpenAI \u2014 enterprise Azure deployments. Base URL is deployment-\n * specific and must be provided via `AZURE_OPENAI_BASE_URL`.\n */\nconst AZURE_PRESET: OpenAICompatiblePreset = {\n id: 'azure',\n name: 'Azure OpenAI',\n baseURL: undefined,\n baseURLEnvKeys: ['AZURE_OPENAI_BASE_URL'],\n envKeys: ['AZURE_OPENAI_API_KEY'],\n defaultModel: 'gpt-5-mini',\n defaultModels: [\n {\n id: 'gpt-5-mini',\n name: 'GPT-5 Mini',\n contextWindow: 128000,\n },\n {\n id: 'gpt-5',\n name: 'GPT-5',\n contextWindow: 128000,\n },\n ],\n}\n\n/**\n * LiteLLM proxy \u2014 self-hosted router for arbitrary upstream providers.\n * Base URL must be supplied via `LITELLM_BASE_URL`.\n */\nconst LITELLM_PRESET: OpenAICompatiblePreset = {\n id: 'litellm',\n name: 'LiteLLM',\n baseURL: 'http://localhost:4000/v1',\n baseURLEnvKeys: ['LITELLM_BASE_URL'],\n envKeys: ['LITELLM_API_KEY'],\n defaultModel: 'gpt-4o-mini',\n defaultModels: [\n {\n id: 'gpt-4o-mini',\n name: 'GPT-4o Mini (via LiteLLM)',\n contextWindow: 128000,\n },\n ],\n}\n\n/**\n * Ollama \u2014 local model runner for development and offline use.\n * Default port 11434 can be overridden via `OLLAMA_BASE_URL`.\n */\nconst OLLAMA_PRESET: OpenAICompatiblePreset = {\n id: 'ollama',\n name: 'Ollama (local)',\n baseURL: 'http://localhost:11434/v1',\n baseURLEnvKeys: ['OLLAMA_BASE_URL'],\n envKeys: ['OLLAMA_API_KEY'],\n defaultModel: 'llama3.3',\n defaultModels: [\n {\n id: 'llama3.3',\n name: 'Llama 3.3 (local)',\n contextWindow: 131072,\n },\n {\n id: 'qwen2.5-coder',\n name: 'Qwen 2.5 Coder (local)',\n contextWindow: 131072,\n tags: ['coding'],\n },\n ],\n}\n\n/**\n * OpenRouter \u2014 unified API gateway providing access to hundreds of models\n * from Anthropic, OpenAI, Google, Meta, and others via a single OpenAI-\n * compatible endpoint.\n */\nconst OPENROUTER_PRESET: OpenAICompatiblePreset = {\n id: 'openrouter',\n name: 'OpenRouter',\n baseURL: 'https://openrouter.ai/api/v1',\n baseURLEnvKeys: ['OPENROUTER_BASE_URL'],\n envKeys: ['OPENROUTER_API_KEY'],\n defaultModel: 'meta-llama/llama-3.3-70b-instruct',\n defaultModels: [\n {\n id: 'meta-llama/llama-3.3-70b-instruct',\n name: 'Llama 3.3 70B Instruct',\n contextWindow: 131072,\n },\n ],\n}\n\n/**\n * LM Studio \u2014 local model server for development and offline use.\n * Default port 1234 can be overridden via `LM_STUDIO_BASE_URL`.\n * `defaultModel` is intentionally empty \u2014 LM Studio auto-detects\n * the loaded model when the request body's `model` field is empty.\n */\nconst LM_STUDIO_PRESET: OpenAICompatiblePreset = {\n id: 'lm-studio',\n name: 'LM Studio (local)',\n baseURL: 'http://localhost:1234/v1',\n baseURLEnvKeys: ['LM_STUDIO_BASE_URL'],\n envKeys: ['LM_STUDIO_API_KEY'],\n defaultModel: '',\n defaultModels: [],\n}\n\n/**\n * Built-in presets registered at bootstrap time. Order matters \u2014 it\n * determines the default iteration order of\n * `llmProviderRegistry.resolveFirstConfigured()` when no explicit order\n * is supplied.\n */\nexport const OPENAI_COMPATIBLE_PRESETS: readonly OpenAICompatiblePreset[] = [\n OPENAI_PRESET,\n DEEPINFRA_PRESET,\n GROQ_PRESET,\n TOGETHER_PRESET,\n FIREWORKS_PRESET,\n AZURE_PRESET,\n LITELLM_PRESET,\n OLLAMA_PRESET,\n OPENROUTER_PRESET,\n LM_STUDIO_PRESET,\n]\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Curated registry of OpenAI-compatible LLM backends.\n *\n * Each preset is plain data \u2014 adding a new backend takes one entry in\n * this array, zero new adapter files, and zero changes to route handlers.\n * The {@link createOpenAICompatibleProvider} factory in `./llm-adapters/openai.ts`\n * turns each preset into a concrete `LlmProvider` at bootstrap time.\n *\n * Preset model catalogs are curated snapshots as of 2026-04-14 and should\n * be updated as upstream catalogs evolve. Users can always override the\n * selected model via the `OPENCODE_MODEL` env var without editing this\n * file.\n *\n * @see ./llm-adapters/openai.ts\n * @see .ai/specs/implemented/2026-04-14-llm-provider-ports-and-adapters.md\n */\n\nimport type { OpenAICompatiblePreset } from './llm-adapters/openai'\n\n/**\n * Standard OpenAI \u2014 default OpenAI API at api.openai.com.\n */\nconst OPENAI_PRESET: OpenAICompatiblePreset = {\n id: 'openai',\n name: 'OpenAI',\n baseURL: undefined,\n baseURLEnvKeys: ['OPENAI_BASE_URL'],\n envKeys: ['OPENAI_API_KEY', 'OPENCODE_OPENAI_API_KEY'],\n defaultModel: 'gpt-5-mini',\n defaultModels: [\n {\n id: 'gpt-5-mini',\n name: 'GPT-5 Mini',\n contextWindow: 128000,\n tags: ['budget'],\n },\n {\n id: 'gpt-5',\n name: 'GPT-5',\n contextWindow: 128000,\n tags: ['flagship'],\n },\n {\n id: 'gpt-4o-mini',\n name: 'GPT-4o Mini',\n contextWindow: 128000,\n tags: ['budget'],\n },\n {\n id: 'gpt-4o',\n name: 'GPT-4o',\n contextWindow: 128000,\n },\n ],\n}\n\n/**\n * DeepInfra \u2014 hosts open-weight flagship models at 3-12\u00D7 lower cost than\n * the native APIs. The curated catalog targets the AI Assistant use case\n * (routing + tool use + conversational chat).\n */\nconst DEEPINFRA_PRESET: OpenAICompatiblePreset = {\n id: 'deepinfra',\n name: 'DeepInfra',\n baseURL: 'https://api.deepinfra.com/v1/openai',\n baseURLEnvKeys: ['DEEPINFRA_BASE_URL'],\n envKeys: ['DEEPINFRA_API_KEY'],\n defaultModel: 'zai-org/GLM-5.1',\n defaultModels: [\n {\n id: 'zai-org/GLM-5.1',\n name: 'GLM-5.1 (Zhipu)',\n contextWindow: 202752,\n tags: ['flagship'],\n },\n {\n id: 'zai-org/GLM-4.7-Flash',\n name: 'GLM-4.7 Flash',\n contextWindow: 202752,\n tags: ['budget'],\n },\n {\n id: 'Qwen/Qwen3-235B-A22B-Instruct-2507',\n name: 'Qwen3 235B (MoE)',\n contextWindow: 262144,\n tags: ['flagship'],\n },\n {\n id: 'meta-llama/Llama-4-Scout-17B-16E-Instruct',\n name: 'Llama 4 Scout',\n contextWindow: 327680,\n },\n {\n id: 'deepseek-ai/DeepSeek-V3.2-Exp',\n name: 'DeepSeek V3.2',\n contextWindow: 163840,\n tags: ['reasoning'],\n },\n {\n id: 'Qwen/Qwen3-Coder-30B-A3B-Instruct',\n name: 'Qwen3 Coder 30B',\n contextWindow: 262144,\n tags: ['coding'],\n },\n ],\n}\n\n/**\n * Groq \u2014 specializes in low-latency inference on LPU hardware.\n * Best suited for snappy tool-use and routing, less so for long reasoning.\n */\nconst GROQ_PRESET: OpenAICompatiblePreset = {\n id: 'groq',\n name: 'Groq',\n baseURL: 'https://api.groq.com/openai/v1',\n baseURLEnvKeys: ['GROQ_BASE_URL'],\n envKeys: ['GROQ_API_KEY'],\n defaultModel: 'llama-3.3-70b-versatile',\n defaultModels: [\n {\n id: 'llama-3.3-70b-versatile',\n name: 'Llama 3.3 70B Versatile',\n contextWindow: 131072,\n },\n {\n id: 'llama-4-scout-17b',\n name: 'Llama 4 Scout 17B',\n contextWindow: 131072,\n },\n {\n id: 'mixtral-8x22b-32768',\n name: 'Mixtral 8x22B',\n contextWindow: 32768,\n },\n ],\n}\n\n/**\n * Together AI \u2014 broad catalog of open-weight models with per-model pricing.\n */\nconst TOGETHER_PRESET: OpenAICompatiblePreset = {\n id: 'together',\n name: 'Together AI',\n baseURL: 'https://api.together.xyz/v1',\n baseURLEnvKeys: ['TOGETHER_BASE_URL'],\n envKeys: ['TOGETHER_API_KEY'],\n defaultModel: 'meta-llama/Llama-3.3-70B-Instruct-Turbo',\n defaultModels: [\n {\n id: 'meta-llama/Llama-3.3-70B-Instruct-Turbo',\n name: 'Llama 3.3 70B Turbo',\n contextWindow: 131072,\n },\n {\n id: 'Qwen/Qwen2.5-72B-Instruct-Turbo',\n name: 'Qwen 2.5 72B Turbo',\n contextWindow: 32768,\n },\n ],\n}\n\n/**\n * Fireworks AI \u2014 fast inference with a curated catalog.\n */\nconst FIREWORKS_PRESET: OpenAICompatiblePreset = {\n id: 'fireworks',\n name: 'Fireworks AI',\n baseURL: 'https://api.fireworks.ai/inference/v1',\n baseURLEnvKeys: ['FIREWORKS_BASE_URL'],\n envKeys: ['FIREWORKS_API_KEY'],\n defaultModel: 'accounts/fireworks/models/llama-v3p3-70b-instruct',\n defaultModels: [\n {\n id: 'accounts/fireworks/models/llama-v3p3-70b-instruct',\n name: 'Llama 3.3 70B',\n contextWindow: 131072,\n },\n ],\n}\n\n/**\n * Azure OpenAI \u2014 enterprise Azure deployments. Base URL is deployment-\n * specific and must be provided via `AZURE_OPENAI_BASE_URL`.\n */\nconst AZURE_PRESET: OpenAICompatiblePreset = {\n id: 'azure',\n name: 'Azure OpenAI',\n baseURL: undefined,\n baseURLEnvKeys: ['AZURE_OPENAI_BASE_URL'],\n envKeys: ['AZURE_OPENAI_API_KEY'],\n defaultModel: 'gpt-5-mini',\n defaultModels: [\n {\n id: 'gpt-5-mini',\n name: 'GPT-5 Mini',\n contextWindow: 128000,\n },\n {\n id: 'gpt-5',\n name: 'GPT-5',\n contextWindow: 128000,\n },\n ],\n}\n\n/**\n * LiteLLM proxy \u2014 self-hosted router for arbitrary upstream providers.\n * Base URL must be supplied via `LITELLM_BASE_URL`.\n */\nconst LITELLM_PRESET: OpenAICompatiblePreset = {\n id: 'litellm',\n name: 'LiteLLM',\n baseURL: 'http://localhost:4000/v1',\n baseURLEnvKeys: ['LITELLM_BASE_URL'],\n envKeys: ['LITELLM_API_KEY'],\n defaultModel: 'gpt-4o-mini',\n defaultModels: [\n {\n id: 'gpt-4o-mini',\n name: 'GPT-4o Mini (via LiteLLM)',\n contextWindow: 128000,\n },\n ],\n}\n\n/**\n * Ollama \u2014 local model runner for development and offline use.\n * Default port 11434 can be overridden via `OLLAMA_BASE_URL`.\n */\nconst OLLAMA_PRESET: OpenAICompatiblePreset = {\n id: 'ollama',\n name: 'Ollama (local)',\n baseURL: 'http://localhost:11434/v1',\n baseURLEnvKeys: ['OLLAMA_BASE_URL'],\n envKeys: ['OLLAMA_API_KEY'],\n defaultModel: 'llama3.3',\n defaultModels: [\n {\n id: 'llama3.3',\n name: 'Llama 3.3 (local)',\n contextWindow: 131072,\n },\n {\n id: 'qwen2.5-coder',\n name: 'Qwen 2.5 Coder (local)',\n contextWindow: 131072,\n tags: ['coding'],\n },\n ],\n}\n\n/**\n * OpenRouter \u2014 unified API gateway providing access to hundreds of models\n * from Anthropic, OpenAI, Google, Meta, and others via a single OpenAI-\n * compatible endpoint.\n */\nconst OPENROUTER_PRESET: OpenAICompatiblePreset = {\n id: 'openrouter',\n name: 'OpenRouter',\n baseURL: 'https://openrouter.ai/api/v1',\n baseURLEnvKeys: ['OPENROUTER_BASE_URL'],\n envKeys: ['OPENROUTER_API_KEY'],\n defaultModel: 'meta-llama/llama-3.3-70b-instruct',\n defaultModels: [\n {\n id: 'meta-llama/llama-3.3-70b-instruct',\n name: 'Llama 3.3 70B Instruct',\n contextWindow: 131072,\n },\n ],\n}\n\n/**\n * Requesty \u2014 unified OpenAI-compatible LLM gateway providing access to\n * models from many providers via a single endpoint, using `provider/model`\n * naming (e.g. `openai/gpt-4o-mini`).\n */\nconst REQUESTY_PRESET: OpenAICompatiblePreset = {\n id: 'requesty',\n name: 'Requesty',\n baseURL: 'https://router.requesty.ai/v1',\n baseURLEnvKeys: ['REQUESTY_BASE_URL'],\n envKeys: ['REQUESTY_API_KEY'],\n defaultModel: 'openai/gpt-4o-mini',\n defaultModels: [\n {\n id: 'openai/gpt-4o-mini',\n name: 'GPT-4o mini',\n contextWindow: 128000,\n },\n ],\n}\n\n/**\n * LM Studio \u2014 local model server for development and offline use.\n * Default port 1234 can be overridden via `LM_STUDIO_BASE_URL`.\n * `defaultModel` is intentionally empty \u2014 LM Studio auto-detects\n * the loaded model when the request body's `model` field is empty.\n */\nconst LM_STUDIO_PRESET: OpenAICompatiblePreset = {\n id: 'lm-studio',\n name: 'LM Studio (local)',\n baseURL: 'http://localhost:1234/v1',\n baseURLEnvKeys: ['LM_STUDIO_BASE_URL'],\n envKeys: ['LM_STUDIO_API_KEY'],\n defaultModel: '',\n defaultModels: [],\n}\n\n/**\n * Built-in presets registered at bootstrap time. Order matters \u2014 it\n * determines the default iteration order of\n * `llmProviderRegistry.resolveFirstConfigured()` when no explicit order\n * is supplied.\n */\nexport const OPENAI_COMPATIBLE_PRESETS: readonly OpenAICompatiblePreset[] = [\n OPENAI_PRESET,\n DEEPINFRA_PRESET,\n GROQ_PRESET,\n TOGETHER_PRESET,\n FIREWORKS_PRESET,\n AZURE_PRESET,\n LITELLM_PRESET,\n OLLAMA_PRESET,\n OPENROUTER_PRESET,\n REQUESTY_PRESET,\n LM_STUDIO_PRESET,\n]\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -105,7 +105,7 @@ function toolToIndexableRecord(tool, moduleId) {
|
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
107
|
function computeToolsChecksum(tools) {
|
|
108
|
-
const content = tools.map((t) => `${t.name}:${t.description}`).sort().join("|");
|
|
108
|
+
const content = tools.map((t) => `${t.name}:${t.description}`).sort((a, b) => a < b ? -1 : a > b ? 1 : 0).join("|");
|
|
109
109
|
let hash = 0;
|
|
110
110
|
for (let i = 0; i < content.length; i++) {
|
|
111
111
|
const char = content.charCodeAt(i);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/ai_assistant/lib/tool-index-config.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n SearchEntityConfig,\n SearchResultPresenter,\n IndexableRecord,\n} from '@open-mercato/search/types'\nimport type { McpToolDefinition } from './types'\n\n/**\n * Entity ID for MCP tools in the search index.\n * Following the module:entity naming convention.\n */\nexport const TOOL_ENTITY_ID = 'ai_assistant:mcp_tool' as const\n\n/**\n * Tenant ID for global tools.\n * Tools are not tenant-scoped, so we use a special \"system\" UUID.\n * This is the nil UUID (all zeros) reserved for system-wide resources.\n */\nexport const GLOBAL_TENANT_ID = '00000000-0000-0000-0000-000000000000'\n\n/**\n * Essential tools that should always be available regardless of search results.\n * These provide fundamental functionality for the AI assistant.\n */\nexport const ESSENTIAL_TOOLS = [\n 'context_whoami', // Auth context awareness\n 'search_query', // Universal search\n 'search_schema', // Entity discovery\n 'search_status', // Check integrations\n] as const\n\n/**\n * Default configuration for tool search.\n */\nexport const TOOL_SEARCH_CONFIG = {\n /** Maximum tools to return from search */\n defaultLimit: 12,\n /** Minimum relevance score (0-1) */\n minScore: 0.2,\n /** Strategies to use (in priority order) */\n strategies: ['fulltext', 'vector', 'tokens'] as const,\n} as const\n\n/**\n * Search entity configuration for MCP tools.\n * This configures how tools are indexed and searched.\n */\nexport const toolSearchEntityConfig: SearchEntityConfig = {\n entityId: TOOL_ENTITY_ID,\n enabled: true,\n priority: 100, // High priority for tool results\n\n /**\n * Build searchable content from a tool definition.\n */\n buildSource: (ctx) => {\n const tool = ctx.record as unknown as McpToolDefinition\n const name = tool.name || ''\n const description = tool.description || ''\n const moduleId = (ctx.record as Record<string, unknown>).moduleId as string | undefined\n\n // Normalize name: replace underscores/dots with spaces for better search\n const normalizedName = name.replace(/[_.-]/g, ' ')\n\n // Build text content for embedding and fulltext search\n const textContent = [\n normalizedName,\n description,\n moduleId ? `module ${moduleId}` : '',\n ]\n .filter(Boolean)\n .join(' | ')\n\n return {\n text: textContent,\n fields: {\n name: normalizedName,\n originalName: name,\n description,\n moduleId: moduleId ?? null,\n requiredFeatures: tool.requiredFeatures ?? [],\n },\n presenter: {\n title: name,\n subtitle: description.slice(0, 100),\n icon: 'tool',\n },\n checksumSource: { name, description, moduleId },\n }\n },\n\n /**\n * Format result for display in search UI.\n */\n formatResult: (ctx) => {\n const tool = ctx.record as unknown as McpToolDefinition\n return {\n title: tool.name || 'Unknown Tool',\n subtitle: (tool.description || '').slice(0, 100),\n icon: 'tool',\n }\n },\n\n /**\n * Field policy for search strategies.\n */\n fieldPolicy: {\n searchable: ['name', 'description', 'moduleId'],\n hashOnly: [],\n excluded: ['requiredFeatures', 'inputSchema', 'handler'],\n },\n}\n\n/**\n * Convert an MCP tool definition to an indexable record for search.\n *\n * @param tool - The tool definition to index\n * @param moduleId - The module that registered this tool\n * @returns IndexableRecord ready for search indexing\n */\nexport function toolToIndexableRecord(\n tool: McpToolDefinition,\n moduleId?: string\n): IndexableRecord {\n const normalizedName = tool.name.replace(/[_.-]/g, ' ')\n const description = tool.description || ''\n\n // Build text for vector embedding\n const embeddingText = `${normalizedName} | ${description}`\n\n const presenter: SearchResultPresenter = {\n title: tool.name,\n subtitle: description.slice(0, 100),\n icon: 'tool',\n }\n\n return {\n entityId: TOOL_ENTITY_ID,\n recordId: tool.name,\n tenantId: GLOBAL_TENANT_ID,\n organizationId: null,\n fields: {\n name: normalizedName,\n originalName: tool.name,\n description,\n moduleId: moduleId ?? null,\n requiredFeatures: tool.requiredFeatures ?? [],\n },\n presenter,\n text: embeddingText,\n checksumSource: {\n name: tool.name,\n description,\n moduleId,\n },\n }\n}\n\n/**\n * Compute a simple checksum for tool definitions.\n * Used to detect changes and avoid unnecessary re-indexing.\n */\nexport function computeToolsChecksum(\n tools: Array<{ name: string; description: string }>\n): string {\n const content = tools\n .map((t) => `${t.name}:${t.description}`)\n .sort()\n .join('|')\n\n // Simple hash using string code points\n let hash = 0\n for (let i = 0; i < content.length; i++) {\n const char = content.charCodeAt(i)\n hash = ((hash << 5) - hash + char) | 0\n }\n return hash.toString(16)\n}\n"],
|
|
5
|
-
"mappings": "AAWO,MAAM,iBAAiB;AAOvB,MAAM,mBAAmB;AAMzB,MAAM,kBAAkB;AAAA,EAC7B;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AAKO,MAAM,qBAAqB;AAAA;AAAA,EAEhC,cAAc;AAAA;AAAA,EAEd,UAAU;AAAA;AAAA,EAEV,YAAY,CAAC,YAAY,UAAU,QAAQ;AAC7C;AAMO,MAAM,yBAA6C;AAAA,EACxD,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,EAKV,aAAa,CAAC,QAAQ;AACpB,UAAM,OAAO,IAAI;AACjB,UAAM,OAAO,KAAK,QAAQ;AAC1B,UAAM,cAAc,KAAK,eAAe;AACxC,UAAM,WAAY,IAAI,OAAmC;AAGzD,UAAM,iBAAiB,KAAK,QAAQ,UAAU,GAAG;AAGjD,UAAM,cAAc;AAAA,MAClB;AAAA,MACA;AAAA,MACA,WAAW,UAAU,QAAQ,KAAK;AAAA,IACpC,EACG,OAAO,OAAO,EACd,KAAK,KAAK;AAEb,WAAO;AAAA,MACL,MAAM;AAAA,MACN,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,cAAc;AAAA,QACd;AAAA,QACA,UAAU,YAAY;AAAA,QACtB,kBAAkB,KAAK,oBAAoB,CAAC;AAAA,MAC9C;AAAA,MACA,WAAW;AAAA,QACT,OAAO;AAAA,QACP,UAAU,YAAY,MAAM,GAAG,GAAG;AAAA,QAClC,MAAM;AAAA,MACR;AAAA,MACA,gBAAgB,EAAE,MAAM,aAAa,SAAS;AAAA,IAChD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,CAAC,QAAQ;AACrB,UAAM,OAAO,IAAI;AACjB,WAAO;AAAA,MACL,OAAO,KAAK,QAAQ;AAAA,MACpB,WAAW,KAAK,eAAe,IAAI,MAAM,GAAG,GAAG;AAAA,MAC/C,MAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa;AAAA,IACX,YAAY,CAAC,QAAQ,eAAe,UAAU;AAAA,IAC9C,UAAU,CAAC;AAAA,IACX,UAAU,CAAC,oBAAoB,eAAe,SAAS;AAAA,EACzD;AACF;AASO,SAAS,sBACd,MACA,UACiB;AACjB,QAAM,iBAAiB,KAAK,KAAK,QAAQ,UAAU,GAAG;AACtD,QAAM,cAAc,KAAK,eAAe;AAGxC,QAAM,gBAAgB,GAAG,cAAc,MAAM,WAAW;AAExD,QAAM,YAAmC;AAAA,IACvC,OAAO,KAAK;AAAA,IACZ,UAAU,YAAY,MAAM,GAAG,GAAG;AAAA,IAClC,MAAM;AAAA,EACR;AAEA,SAAO;AAAA,IACL,UAAU;AAAA,IACV,UAAU,KAAK;AAAA,IACf,UAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,cAAc,KAAK;AAAA,MACnB;AAAA,MACA,UAAU,YAAY;AAAA,MACtB,kBAAkB,KAAK,oBAAoB,CAAC;AAAA,IAC9C;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN,gBAAgB;AAAA,MACd,MAAM,KAAK;AAAA,MACX;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAMO,SAAS,qBACd,OACQ;AACR,QAAM,UAAU,MACb,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,IAAI,EAAE,WAAW,EAAE,EACvC,KAAK,
|
|
4
|
+
"sourcesContent": ["import type {\n SearchEntityConfig,\n SearchResultPresenter,\n IndexableRecord,\n} from '@open-mercato/search/types'\nimport type { McpToolDefinition } from './types'\n\n/**\n * Entity ID for MCP tools in the search index.\n * Following the module:entity naming convention.\n */\nexport const TOOL_ENTITY_ID = 'ai_assistant:mcp_tool' as const\n\n/**\n * Tenant ID for global tools.\n * Tools are not tenant-scoped, so we use a special \"system\" UUID.\n * This is the nil UUID (all zeros) reserved for system-wide resources.\n */\nexport const GLOBAL_TENANT_ID = '00000000-0000-0000-0000-000000000000'\n\n/**\n * Essential tools that should always be available regardless of search results.\n * These provide fundamental functionality for the AI assistant.\n */\nexport const ESSENTIAL_TOOLS = [\n 'context_whoami', // Auth context awareness\n 'search_query', // Universal search\n 'search_schema', // Entity discovery\n 'search_status', // Check integrations\n] as const\n\n/**\n * Default configuration for tool search.\n */\nexport const TOOL_SEARCH_CONFIG = {\n /** Maximum tools to return from search */\n defaultLimit: 12,\n /** Minimum relevance score (0-1) */\n minScore: 0.2,\n /** Strategies to use (in priority order) */\n strategies: ['fulltext', 'vector', 'tokens'] as const,\n} as const\n\n/**\n * Search entity configuration for MCP tools.\n * This configures how tools are indexed and searched.\n */\nexport const toolSearchEntityConfig: SearchEntityConfig = {\n entityId: TOOL_ENTITY_ID,\n enabled: true,\n priority: 100, // High priority for tool results\n\n /**\n * Build searchable content from a tool definition.\n */\n buildSource: (ctx) => {\n const tool = ctx.record as unknown as McpToolDefinition\n const name = tool.name || ''\n const description = tool.description || ''\n const moduleId = (ctx.record as Record<string, unknown>).moduleId as string | undefined\n\n // Normalize name: replace underscores/dots with spaces for better search\n const normalizedName = name.replace(/[_.-]/g, ' ')\n\n // Build text content for embedding and fulltext search\n const textContent = [\n normalizedName,\n description,\n moduleId ? `module ${moduleId}` : '',\n ]\n .filter(Boolean)\n .join(' | ')\n\n return {\n text: textContent,\n fields: {\n name: normalizedName,\n originalName: name,\n description,\n moduleId: moduleId ?? null,\n requiredFeatures: tool.requiredFeatures ?? [],\n },\n presenter: {\n title: name,\n subtitle: description.slice(0, 100),\n icon: 'tool',\n },\n checksumSource: { name, description, moduleId },\n }\n },\n\n /**\n * Format result for display in search UI.\n */\n formatResult: (ctx) => {\n const tool = ctx.record as unknown as McpToolDefinition\n return {\n title: tool.name || 'Unknown Tool',\n subtitle: (tool.description || '').slice(0, 100),\n icon: 'tool',\n }\n },\n\n /**\n * Field policy for search strategies.\n */\n fieldPolicy: {\n searchable: ['name', 'description', 'moduleId'],\n hashOnly: [],\n excluded: ['requiredFeatures', 'inputSchema', 'handler'],\n },\n}\n\n/**\n * Convert an MCP tool definition to an indexable record for search.\n *\n * @param tool - The tool definition to index\n * @param moduleId - The module that registered this tool\n * @returns IndexableRecord ready for search indexing\n */\nexport function toolToIndexableRecord(\n tool: McpToolDefinition,\n moduleId?: string\n): IndexableRecord {\n const normalizedName = tool.name.replace(/[_.-]/g, ' ')\n const description = tool.description || ''\n\n // Build text for vector embedding\n const embeddingText = `${normalizedName} | ${description}`\n\n const presenter: SearchResultPresenter = {\n title: tool.name,\n subtitle: description.slice(0, 100),\n icon: 'tool',\n }\n\n return {\n entityId: TOOL_ENTITY_ID,\n recordId: tool.name,\n tenantId: GLOBAL_TENANT_ID,\n organizationId: null,\n fields: {\n name: normalizedName,\n originalName: tool.name,\n description,\n moduleId: moduleId ?? null,\n requiredFeatures: tool.requiredFeatures ?? [],\n },\n presenter,\n text: embeddingText,\n checksumSource: {\n name: tool.name,\n description,\n moduleId,\n },\n }\n}\n\n/**\n * Compute a simple checksum for tool definitions.\n * Used to detect changes and avoid unnecessary re-indexing.\n */\nexport function computeToolsChecksum(\n tools: Array<{ name: string; description: string }>\n): string {\n const content = tools\n .map((t) => `${t.name}:${t.description}`)\n .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))\n .join('|')\n\n // Simple hash using string code points\n let hash = 0\n for (let i = 0; i < content.length; i++) {\n const char = content.charCodeAt(i)\n hash = ((hash << 5) - hash + char) | 0\n }\n return hash.toString(16)\n}\n"],
|
|
5
|
+
"mappings": "AAWO,MAAM,iBAAiB;AAOvB,MAAM,mBAAmB;AAMzB,MAAM,kBAAkB;AAAA,EAC7B;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AAKO,MAAM,qBAAqB;AAAA;AAAA,EAEhC,cAAc;AAAA;AAAA,EAEd,UAAU;AAAA;AAAA,EAEV,YAAY,CAAC,YAAY,UAAU,QAAQ;AAC7C;AAMO,MAAM,yBAA6C;AAAA,EACxD,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,EAKV,aAAa,CAAC,QAAQ;AACpB,UAAM,OAAO,IAAI;AACjB,UAAM,OAAO,KAAK,QAAQ;AAC1B,UAAM,cAAc,KAAK,eAAe;AACxC,UAAM,WAAY,IAAI,OAAmC;AAGzD,UAAM,iBAAiB,KAAK,QAAQ,UAAU,GAAG;AAGjD,UAAM,cAAc;AAAA,MAClB;AAAA,MACA;AAAA,MACA,WAAW,UAAU,QAAQ,KAAK;AAAA,IACpC,EACG,OAAO,OAAO,EACd,KAAK,KAAK;AAEb,WAAO;AAAA,MACL,MAAM;AAAA,MACN,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,cAAc;AAAA,QACd;AAAA,QACA,UAAU,YAAY;AAAA,QACtB,kBAAkB,KAAK,oBAAoB,CAAC;AAAA,MAC9C;AAAA,MACA,WAAW;AAAA,QACT,OAAO;AAAA,QACP,UAAU,YAAY,MAAM,GAAG,GAAG;AAAA,QAClC,MAAM;AAAA,MACR;AAAA,MACA,gBAAgB,EAAE,MAAM,aAAa,SAAS;AAAA,IAChD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,CAAC,QAAQ;AACrB,UAAM,OAAO,IAAI;AACjB,WAAO;AAAA,MACL,OAAO,KAAK,QAAQ;AAAA,MACpB,WAAW,KAAK,eAAe,IAAI,MAAM,GAAG,GAAG;AAAA,MAC/C,MAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa;AAAA,IACX,YAAY,CAAC,QAAQ,eAAe,UAAU;AAAA,IAC9C,UAAU,CAAC;AAAA,IACX,UAAU,CAAC,oBAAoB,eAAe,SAAS;AAAA,EACzD;AACF;AASO,SAAS,sBACd,MACA,UACiB;AACjB,QAAM,iBAAiB,KAAK,KAAK,QAAQ,UAAU,GAAG;AACtD,QAAM,cAAc,KAAK,eAAe;AAGxC,QAAM,gBAAgB,GAAG,cAAc,MAAM,WAAW;AAExD,QAAM,YAAmC;AAAA,IACvC,OAAO,KAAK;AAAA,IACZ,UAAU,YAAY,MAAM,GAAG,GAAG;AAAA,IAClC,MAAM;AAAA,EACR;AAEA,SAAO;AAAA,IACL,UAAU;AAAA,IACV,UAAU,KAAK;AAAA,IACf,UAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,cAAc,KAAK;AAAA,MACnB;AAAA,MACA,UAAU,YAAY;AAAA,MACtB,kBAAkB,KAAK,oBAAoB,CAAC;AAAA,IAC9C;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN,gBAAgB;AAAA,MACd,MAAM,KAAK;AAAA,MACX;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAMO,SAAS,qBACd,OACQ;AACR,QAAM,UAAU,MACb,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,IAAI,EAAE,WAAW,EAAE,EACvC,KAAK,CAAC,GAAG,MAAO,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAE,EAC3C,KAAK,GAAG;AAGX,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,OAAO,QAAQ,WAAW,CAAC;AACjC,YAAS,QAAQ,KAAK,OAAO,OAAQ;AAAA,EACvC;AACA,SAAO,KAAK,SAAS,EAAE;AACzB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/ai-assistant",
|
|
3
|
-
"version": "0.6.6-develop.
|
|
3
|
+
"version": "0.6.6-develop.6344.1.c4b17c07c4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -99,16 +99,16 @@
|
|
|
99
99
|
"zod-to-json-schema": "^3.25.2"
|
|
100
100
|
},
|
|
101
101
|
"peerDependencies": {
|
|
102
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
103
|
-
"@open-mercato/ui": "0.6.6-develop.
|
|
102
|
+
"@open-mercato/shared": "0.6.6-develop.6344.1.c4b17c07c4",
|
|
103
|
+
"@open-mercato/ui": "0.6.6-develop.6344.1.c4b17c07c4",
|
|
104
104
|
"react": "^19.0.0",
|
|
105
105
|
"react-dom": "^19.0.0",
|
|
106
106
|
"zod": ">=3.23.0"
|
|
107
107
|
},
|
|
108
108
|
"devDependencies": {
|
|
109
|
-
"@open-mercato/cli": "0.6.6-develop.
|
|
110
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
111
|
-
"@open-mercato/ui": "0.6.6-develop.
|
|
109
|
+
"@open-mercato/cli": "0.6.6-develop.6344.1.c4b17c07c4",
|
|
110
|
+
"@open-mercato/shared": "0.6.6-develop.6344.1.c4b17c07c4",
|
|
111
|
+
"@open-mercato/ui": "0.6.6-develop.6344.1.c4b17c07c4",
|
|
112
112
|
"@types/react": "^19.2.17",
|
|
113
113
|
"@types/react-dom": "^19.2.3",
|
|
114
114
|
"react": "19.2.7",
|
|
@@ -190,13 +190,13 @@ const listTools: ModuleCli = {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
// Sort modules alphabetically
|
|
193
|
-
const sortedModules = Array.from(byModule.keys()).sort()
|
|
193
|
+
const sortedModules = Array.from(byModule.keys()).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))
|
|
194
194
|
|
|
195
195
|
for (const module of sortedModules) {
|
|
196
196
|
const tools = byModule.get(module)!
|
|
197
197
|
console.log(`${module} (${tools.length} tools):`)
|
|
198
198
|
|
|
199
|
-
for (const name of tools.sort()) {
|
|
199
|
+
for (const name of tools.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))) {
|
|
200
200
|
const tool = registry.getTool(name)
|
|
201
201
|
if (!tool) continue
|
|
202
202
|
|
|
@@ -327,7 +327,7 @@ const testTools: ModuleCli = {
|
|
|
327
327
|
list.push(record)
|
|
328
328
|
byModule.set(record.module, list)
|
|
329
329
|
}
|
|
330
|
-
const sortedModules = Array.from(byModule.keys()).sort()
|
|
330
|
+
const sortedModules = Array.from(byModule.keys()).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))
|
|
331
331
|
for (const moduleId of sortedModules) {
|
|
332
332
|
const list = byModule.get(moduleId)!
|
|
333
333
|
console.log('')
|
|
@@ -341,6 +341,6 @@ export function getGraphStats(graph: EntityGraph): {
|
|
|
341
341
|
return {
|
|
342
342
|
totalEntities: graph.nodes.length,
|
|
343
343
|
totalRelationships: graph.edges.length,
|
|
344
|
-
modules: Array.from(byModule.keys()).sort(),
|
|
344
|
+
modules: Array.from(byModule.keys()).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)),
|
|
345
345
|
}
|
|
346
346
|
}
|
|
@@ -189,7 +189,7 @@ export function computeEntitiesChecksum(
|
|
|
189
189
|
): string {
|
|
190
190
|
const content = entities
|
|
191
191
|
.map((e) => `${e.className}:${e.tableName}:${e.fieldCount}`)
|
|
192
|
-
.sort()
|
|
192
|
+
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))
|
|
193
193
|
.join('|')
|
|
194
194
|
|
|
195
195
|
// Simple hash using string code points
|
|
@@ -270,6 +270,27 @@ const OPENROUTER_PRESET: OpenAICompatiblePreset = {
|
|
|
270
270
|
],
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Requesty — unified OpenAI-compatible LLM gateway providing access to
|
|
275
|
+
* models from many providers via a single endpoint, using `provider/model`
|
|
276
|
+
* naming (e.g. `openai/gpt-4o-mini`).
|
|
277
|
+
*/
|
|
278
|
+
const REQUESTY_PRESET: OpenAICompatiblePreset = {
|
|
279
|
+
id: 'requesty',
|
|
280
|
+
name: 'Requesty',
|
|
281
|
+
baseURL: 'https://router.requesty.ai/v1',
|
|
282
|
+
baseURLEnvKeys: ['REQUESTY_BASE_URL'],
|
|
283
|
+
envKeys: ['REQUESTY_API_KEY'],
|
|
284
|
+
defaultModel: 'openai/gpt-4o-mini',
|
|
285
|
+
defaultModels: [
|
|
286
|
+
{
|
|
287
|
+
id: 'openai/gpt-4o-mini',
|
|
288
|
+
name: 'GPT-4o mini',
|
|
289
|
+
contextWindow: 128000,
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
}
|
|
293
|
+
|
|
273
294
|
/**
|
|
274
295
|
* LM Studio — local model server for development and offline use.
|
|
275
296
|
* Default port 1234 can be overridden via `LM_STUDIO_BASE_URL`.
|
|
@@ -302,5 +323,6 @@ export const OPENAI_COMPATIBLE_PRESETS: readonly OpenAICompatiblePreset[] = [
|
|
|
302
323
|
LITELLM_PRESET,
|
|
303
324
|
OLLAMA_PRESET,
|
|
304
325
|
OPENROUTER_PRESET,
|
|
326
|
+
REQUESTY_PRESET,
|
|
305
327
|
LM_STUDIO_PRESET,
|
|
306
328
|
]
|