diff --git a/README.md b/README.md new file mode 100644 index 0000000..51d02b2 --- /dev/null +++ b/README.md @@ -0,0 +1,99 @@ +# Search Agent + +An intelligent search service with REST API, CLI, and MCP server interfaces. + +## Features + +- **REST API**: Full-featured HTTP API with streaming support +- **CLI**: Command-line interface for quick searches +- **MCP Server**: Model Context Protocol server for integration with AI assistants + +## Installation + +```bash +npm install +``` + +## Usage + +### REST API Server + +```bash +npm run serve +``` + +The server will start at `http://localhost:3000`. + +### CLI + +```bash +npm run search "Your question here" +``` + +### MCP Server + +```bash +npm run mcp +``` + +## MCP Configuration + +To use the search agent as an MCP server in your AI assistant, add this to your configuration: + +### Claude Desktop + +Add to `claude_desktop_config.json`: + +```json +{ + "mcpServers": { + "search-agent": { + "command": "node", + "args": ["/path/to/searchAgent/mcpServer.js"], + "env": { + "EXA_API_KEY": "your-exa-api-key", + "OPENROUTER_API_KEY": "your-openrouter-api-key" + } + } + } +} +``` + +### Other MCP Clients + +For other MCP clients, configure the command as: +- Command: `node` +- Args: `["/path/to/searchAgent/mcpServer.js"]` +- Environment variables: `EXA_API_KEY` and `OPENROUTER_API_KEY` + +## Available MCP Tools + +### search + +Search for information and return a summarized answer with sources. + +**Parameters:** +- `question` (required): The question or topic to search for +- `previousClarification` (optional): Previous clarification from the user if the question needed refinement +- `originalQuestion` (optional): The original question if this is a follow-up + +**Example:** +```json +{ + "name": "search", + "arguments": { + "question": "What are the latest developments in AI?" + } +} +``` + +## Environment Variables + +Required: +- `EXA_API_KEY`: Your Exa API key +- `OPENROUTER_API_KEY`: Your OpenRouter API key + +Optional: +- `PORT`: Port for REST server (default: 3000) +- `HOST`: Host for REST server (default: 0.0.0.0) +- `MAINTENANCE_MODE`: Set to "true" to enable maintenance mode diff --git a/mcpServer.js b/mcpServer.js new file mode 100755 index 0000000..e41c80e --- /dev/null +++ b/mcpServer.js @@ -0,0 +1,11 @@ +#!/home/seb/.nvm/versions/node/v22.15.1/bin/node +import dotenv from 'dotenv'; +import { startMCPServer } from './src/mcpServer.js'; + +// Load environment variables from .env file +dotenv.config(); + +startMCPServer().catch((error) => { + console.error('Failed to start MCP server:', error); + process.exit(1); +}); diff --git a/package-lock.json b/package-lock.json index 996283e..21fd1b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@modelcontextprotocol/sdk": "^1.0.4", "@openrouter/sdk": "^0.11.2", "chalk": "^5.6.2", "dotenv": "^17.2.3", @@ -17,6 +18,58 @@ "tiktoken": "^1.0.22" } }, + "node_modules/@hono/node-server": { + "version": "1.19.12", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.12.tgz", + "integrity": "sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, "node_modules/@openrouter/sdk": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/@openrouter/sdk/-/sdk-0.11.2.tgz", @@ -40,6 +93,39 @@ "node": ">= 0.6" } }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/body-parser": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", @@ -154,6 +240,23 @@ "node": ">=6.6.0" } }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/cross-fetch": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", @@ -163,6 +266,20 @@ "node-fetch": "^2.7.0" } }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -275,6 +392,27 @@ "node": ">= 0.6" } }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/exa-js": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/exa-js/-/exa-js-2.11.0.tgz", @@ -343,6 +481,46 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-rate-limit": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.2.tgz", + "integrity": "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==", + "license": "MIT", + "dependencies": { + "ip-address": "10.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/finalhandler": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", @@ -464,6 +642,15 @@ "node": ">= 0.4" } }, + "node_modules/hono": { + "version": "4.12.10", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.10.tgz", + "integrity": "sha512-mx/p18PLy5og9ufies2GOSUqep98Td9q4i/EF6X7yJgAiIopxqdfIO3jbqsi3jRgTgw88jMDEzVKi+V2EF+27w==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -506,6 +693,15 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -521,6 +717,33 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -611,6 +834,15 @@ } } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -674,6 +906,15 @@ "node": ">= 0.8" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-to-regexp": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", @@ -684,6 +925,15 @@ "url": "https://opencollective.com/express" } }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -736,6 +986,15 @@ "node": ">= 0.10" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -809,6 +1068,27 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -959,6 +1239,21 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index d00d392..45e9671 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,14 @@ "scripts": { "serve": "node restSearch.js", "search": "node searchCLI.js", + "mcp": "node mcpServer.js", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "description": "", "dependencies": { + "@modelcontextprotocol/sdk": "^1.0.4", "@openrouter/sdk": "^0.11.2", "chalk": "^5.6.2", "dotenv": "^17.2.3", diff --git a/public/index.html b/public/index.html index 7e6c1d1..2c49bc7 100644 --- a/public/index.html +++ b/public/index.html @@ -545,7 +545,7 @@

Beispiel Anfragen:

- + diff --git a/src/mcpServer.js b/src/mcpServer.js new file mode 100644 index 0000000..ab83c23 --- /dev/null +++ b/src/mcpServer.js @@ -0,0 +1,124 @@ +import { Server } from '@modelcontextprotocol/sdk/server/index.js'; +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import { + CallToolRequestSchema, + ListToolsRequestSchema, +} from '@modelcontextprotocol/sdk/types.js'; +import { createClients } from './clients.js'; +import { getConfig, validateConfig } from './config/env.js'; +import { createSearchService } from './services/searchService.js'; + +function createSimpleBroadcast() { + return (message, type = 'info', data = null) => { + // Log to stderr so it doesn't interfere with MCP protocol on stdout + const timestamp = new Date().toLocaleTimeString(); + const prefix = type === 'error' ? '❌' : type === 'warning' ? '⚠️' : type === 'success' ? '✅' : 'ℹ️'; + console.error(`[${timestamp}] ${prefix} ${message}`); + }; +} + +export async function startMCPServer() { + const config = getConfig(); + validateConfig(config); + + const clients = createClients(config); + const broadcast = createSimpleBroadcast(); + const searchService = createSearchService({ + ...clients, + broadcast, + }); + + const server = new Server( + { + name: 'search-agent', + version: '1.0.0', + }, + { + capabilities: { + tools: {}, + }, + }, + ); + + // Handler for listing available tools + server.setRequestHandler(ListToolsRequestSchema, async () => { + return { + tools: [ + { + name: 'search', + description: 'Deep Web Research', + inputSchema: { + type: 'object', + properties: { + question: { + type: 'string', + description: 'The question or topic to search for', + }, + previousClarification: { + type: 'string', + description: 'Optional: Previous clarification from the user if the question needed refinement', + }, + originalQuestion: { + type: 'string', + description: 'Optional: The original question if this is a follow-up', + }, + }, + required: ['question'], + }, + }, + ], + }; + }); + + // Handler for calling tools + server.setRequestHandler(CallToolRequestSchema, async (request) => { + const { name, arguments: args } = request.params; + + if (name !== 'search') { + throw new Error(`Unknown tool: ${name}`); + } + + const { question, previousClarification, originalQuestion } = args; + + if (!question) { + throw new Error('Missing required parameter: question'); + } + + try { + broadcast(`MCP: Starting search for: "${question}"`, 'info'); + + const result = await searchService.search(question, previousClarification, originalQuestion); + + broadcast('MCP: Search completed successfully', 'success'); + + // Return the result in a structured format + return { + content: [ + { + type: 'text', + text: result.fullAnswerHTMLSnippet || 'No answer generated', + }, + ], + isError: false, + }; + } catch (error) { + broadcast(`MCP: Error: ${error.message}`, 'error'); + + return { + content: [ + { + type: 'text', + text: `Error: ${error.message}`, + }, + ], + isError: true, + }; + } + }); + + // Start the server with stdio transport + const transport = new StdioServerTransport(); + await server.connect(transport); + + console.error('🔌 MCP Search Agent server running on stdio'); +} diff --git a/src/services/openRouterService.js b/src/services/openRouterService.js index 5a5331d..416c3b3 100644 --- a/src/services/openRouterService.js +++ b/src/services/openRouterService.js @@ -129,7 +129,7 @@ export async function rephraseQuestion({ question, previousClarification, origin The user has provided this clarification "${previousClarification}" to the original question: ` + originalQuestion; const params = { - model: 'openai/gpt-5.4-mini', + model: 'z-ai/glm-4.7:nitro', messages: [ { role: 'system', content: prompt }, { role: 'user', content: question }, @@ -173,7 +173,7 @@ export async function rephraseQuestion({ question, previousClarification, origin `; const params = { - model: 'openai/gpt-5.4-mini', + model: 'z-ai/glm-4.7:nitro', messages: [ { role: 'system', content: prompt }, { role: 'user', content: question }, @@ -258,16 +258,14 @@ export async function summarizeFinalAnswer({ openrouter, text, question }) { const enrichmentPrompt = ` You are a content formatter. Take the following summary and enrich it with emojis and HTML tags. Allowed tags: , , ,
, 
    ,
  • , ,

    ,

    ,
    - Make it visually appealing and easy to read. Keep the same language as the original summary. - Summary to enrich: - ${summaryData.data.summary} + Make it visually appealing and easy to read. `; const enrichmentParams = { - model: 'kwaipilot/kat-coder-pro-v2', + model: 'qwen/qwen3-235b-a22b-2507:nitro', messages: [ { role: 'system', content: enrichmentPrompt }, - { role: 'user', content: 'Please enrich the summary with HTML tags and emojis.' }, + { role: 'user', content: summaryData.data.summary }, ], reasoning: { effort: 'low' }, response_format: { @@ -314,7 +312,7 @@ export async function summarizeFinalAnswer({ openrouter, text, question }) { }, { name: 'enrichment', - model: 'kwaipilot/kat-coder-pro-v2', + model: 'qwen/qwen3-235b-a22b-2507:nitro', cost: enrichmentData.cost, promptTokens: enrichmentData.prompt_tokens, completionTokens: enrichmentData.completion_tokens, diff --git a/src/services/searchService.js b/src/services/searchService.js index 9b62b1c..c810235 100644 --- a/src/services/searchService.js +++ b/src/services/searchService.js @@ -147,7 +147,7 @@ export function createSearchService({ exa, openrouter, broadcast }) { amount: rephraseResult.cost, prompt_tokens: rephraseResult.prompt_tokens, completion_tokens: rephraseResult.completion_tokens, - model: 'openai/gpt-5.4-mini' + model: 'z-ai/glm-4.7:nitro' }); } catch (error) { throw new SearchServiceError('Failed to generate summary', 500, error);