Title: Duplicate tool calls during conversation - Need better approach than manual caching · Issue #6 · VapiAI/example-server-javascript-node · GitHub
Open Graph Title: Duplicate tool calls during conversation - Need better approach than manual caching · Issue #6 · VapiAI/example-server-javascript-node
X Title: Duplicate tool calls during conversation - Need better approach than manual caching · Issue #6 · VapiAI/example-server-javascript-node
Description: Current Issue: I'm experiencing multiple identical tool calls being sent to my backend API during a single conversation. My backend receives repeated calls with the same parameters throughout a conversation, leading to duplicate processi...
Open Graph Description: Current Issue: I'm experiencing multiple identical tool calls being sent to my backend API during a single conversation. My backend receives repeated calls with the same parameters throughout a con...
X Description: Current Issue: I'm experiencing multiple identical tool calls being sent to my backend API during a single conversation. My backend receives repeated calls with the same parameters throughout a...
Opengraph URL: https://github.com/VapiAI/example-server-javascript-node/issues/6
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Duplicate tool calls during conversation - Need better approach than manual caching","articleBody":"Current Issue: I'm experiencing multiple identical tool calls being sent to my backend API during a single conversation. My backend receives repeated calls with the same parameters throughout a conversation, leading to duplicate processing.\n\nThe issue here is on each message it sends call to my backend that causes tools calls duplication if we have something in configuration the agent only send req to api call when a tool the functions we provide to that agent configuration is called or how can i manage this \n\n`app.post('/api/tools', async (req, res) =\u003e {\n try {\n logger.info('POST /api/tools endpoint called');\n \n // Enhanced logging with request identification\n const requestId = `req-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`;\n logger.debug(`Processing request ${requestId}`, { body: req.body });\n \n // Check if this is a handshake or health check request (empty or minimal payload)\n if (!req.body || (Object.keys(req.body).length === 0)) {\n logger.info('Received handshake/health check request');\n return res.json({\n status: 'ok',\n message: 'Server is operational'\n });\n }\n\n // Check if this is a status update message\n if (req.body.message \u0026\u0026 req.body.message.type) {\n if (req.body.message.type === 'status-update') {\n logger.info(`Status update received: ${req.body.message.status}`);\n logger.info(`Reason: ${req.body.message.endedReason || 'N/A'}`);\n return res.json({\n results: [{\n result: `Received status update: ${req.body.message.status}`\n }]\n });\n } else if (req.body.message.type === 'end-of-call-report') {\n logger.info(`End of call report received`);\n \n // Log details of the call report for analysis\n if (req.body.message.analysis) {\n logger.info('Call analysis', {\n summary: req.body.message.analysis.summary,\n success: req.body.message.analysis.successEvaluation\n });\n }\n \n return res.json({\n results: [{\n result: `Received end of call report`\n }]\n });\n }\n }\n\n // Extract tool calls from the request body - handling different formats\n const { toolCalls, format } = extractToolCallsWithFormat(req.body);\n \n if (!toolCalls || !Array.isArray(toolCalls) || toolCalls.length === 0) {\n logger.warn(`No valid tool calls found in request ${requestId}`, { body: req.body });\n return res.status(400).json({ \n error: 'No valid tool calls found in request',\n requestId: requestId \n });\n }\n\n logger.info(`Found ${toolCalls.length} tool calls in ${format} format`, { requestId });\n\n // Handle regular tool calls\n const results = await Promise.all(toolCalls.map(async (toolCall) =\u003e {\n // Check for duplicate tool calls before processing\n if (isDuplicateToolCall(toolCall)) {\n // Return cached response for duplicate call\n return {\n toolCallId: toolCall.id,\n result: JSON.stringify({\n status: 'duplicate',\n message: 'This tool call was already processed recently.'\n })\n };\n }\n \n const result = await handleToolCall(toolCall);\n return {\n toolCallId: toolCall.id,\n result: result\n };\n }));\n\n logger.info(`Response sent for request ${requestId}`, { results });\n res.json({ results });\n } catch (error) {\n logger.error('Error processing tool call', { error: error.message, stack: error.stack });\n // Send a properly formatted response even on error\n res.json({\n results: [{\n toolCallId: req.body.toolCallId || \"unknown\",\n result: JSON.stringify({\n status: 'error',\n message: error.message\n })\n }]\n });\n }\n});`\n\n\n`import { CreateAssistantDTO } from \"@vapi-ai/web/dist/api\";\nexport const characterAssistant: CreateAssistantDTO = {\n name: \"Mohammad\",\n model: {\n provider: \"openai\",\n model: \"gpt-3.5-turbo\",\n temperature: 0.7,\n messages: [\n {\n role: \"system\",\n content: \"You are a helpful, polite restaurant assistant named Mohammad for a premium Middle Eastern dining restaurant called Khayal. Handle guest interactions professionally in both English (EN) and Arabic (AR), including table reservations, order placement, and menu navigation. If the user speaks Arabic, respond in Arabic. If the user speaks English, respond in English. Always provide clear options and summarize selections. If unsure, ask clarifying questions. You should use the makeReservation function when a customer wants to book a table, the addToOrder function when they want to order food, and the checkAvailability function to verify if tables are available for specific dates and times.\"\n }\n ],\n tools: [\n {\n type: \"function\",\n function: {\n name: \"makeReservation\",\n description: \"Books a table reservation for customers at the restaurant\",\n parameters: {\n type: \"object\",\n properties: {\n guestCount: {\n type: \"number\",\n description: \"Number of guests for the reservation\",\n },\n date: {\n type: \"string\",\n description: \"Date of the reservation (YYYY-MM-DD format)\",\n },\n time: {\n type: \"string\",\n description: \"Time of the reservation (HH:MM format)\",\n },\n name: {\n type: \"string\",\n description: \"Name under which the reservation is made\",\n },\n phoneNumber: {\n type: \"string\",\n description: \"Contact phone number for the reservation\",\n },\n specialRequests: {\n type: \"string\",\n description: \"Any special requests for the reservation\",\n },\n },\n required: [\"guestCount\", \"date\", \"time\", \"name\", \"phoneNumber\"],\n },\n },\n async: false\n },\n {\n type: \"function\",\n function: {\n name: \"addToOrder\",\n description: \"Adds items to a customer's order\",\n parameters: {\n type: \"object\",\n properties: {\n items: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n name: {\n type: \"string\",\n description: \"Name of the menu item\",\n },\n quantity: {\n type: \"number\",\n description: \"Quantity of the item\",\n },\n specialInstructions: {\n type: \"string\",\n description: \"Special instructions for preparing the item\",\n },\n },\n required: [\"name\", \"quantity\"],\n },\n description: \"List of items to add to the order\",\n },\n },\n required: [\"items\"],\n },\n },\n async: false\n },\n {\n type: \"function\",\n function: {\n name: \"checkAvailability\",\n description: \"Checks if tables are available for a specific date and time\",\n parameters: {\n type: \"object\",\n properties: {\n guestCount: {\n type: \"number\",\n description: \"Number of guests\",\n },\n date: {\n type: \"string\",\n description: \"Date to check availability (YYYY-MM-DD format)\",\n },\n time: {\n type: \"string\",\n description: \"Time to check availability (HH:MM format)\",\n },\n },\n required: [\"guestCount\", \"date\", \"time\"],\n },\n },\n async: false\n }\n ],\n },\n \n // Add server configuration for function handling\n server: {\n url: \"https://1dd0-2a09-bac1-5b20-28-00-19b-fe.ngrok-free.app/api/tools\",\n timeoutSeconds: 20,\n secret: \"mock-secret-key\"\n },\n serverUrl: \"https://1dd0-2a09-bac1-5b20-28-00-19b-fe.ngrok-free.app/api/tools\",\n serverUrlSecret:\"mock\",\n \n // Move idle messages to messagePlan instead of the root-level messages property\n messagePlan: {\n idleMessages: [\n \"Please wait while I process your request...\",\n \"I'm checking our system for that information...\",\n \"Just a moment while I update our records...\"\n ],\n idleMessageMaxSpokenCount: 1,\n idleTimeoutSeconds: 5\n },\n \n voice: {\n provider: \"11labs\",\n voiceId: \"paula\",\n },\n \n firstMessage: \"Thank you for calling Khayal Restaurant. This is Mohammad, your Arabic and English scheduling assistant. How may I help you today?\",\n};\n`\n\nThe Problem: While this manual caching works somewhat, I have several concerns:\n\nThis feels like a workaround rather than the right solution\nI'm not sure if 6 seconds is the right deduplication window\nThe manual cache management is not ideal and could lead to memory issues\nI shouldn't need to handle this level of deduplication on my backend\nMy Configuration: I've configured my agent to call my backend tools at http://localhost:3000/api/tools and implemented handlers for various tool functions (makeReservation, addToOrder, checkAvailability). The agent correctly formats and sends tool calls, but I'm receiving duplicate requests.\n\nQuestions:\n\nHow can i configure in some way so my backend is only called when a tool is called?\nYou can ignore these caching things i have mentioned if those are unrelated.\nIs there a configuration setting to prevent the agent from sending duplicate tool calls?\nIs there a recommended backend approach for handling this situation that doesn't require manual caching?\nIs this expected behavior, and if so, what's the recommended pattern for backend implementation?\nCould this be related to how I'm responding to tool calls?\ncan we have a sample configuration of a sample node js server and agent config as well","author":{"url":"https://github.com/umerfarok","@type":"Person","name":"umerfarok"},"datePublished":"2025-04-10T08:34:33.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"url":"https://github.com/6/example-server-javascript-node/issues/6"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:0d0f77e6-b355-ebd1-47c1-afd19180dff6 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | B3DC:2582C9:99C9CD:CC16BF:697BF63E |
| html-safe-nonce | 8ec80463b8c945de53c4d7c319fee09656aea88a845e7a4e7d6a5c8db8aa7ff7 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJCM0RDOjI1ODJDOTo5OUM5Q0Q6Q0MxNkJGOjY5N0JGNjNFIiwidmlzaXRvcl9pZCI6IjUxMzU2NzE0MDMyNDM4OTQzMzQiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 2321afe2496608aaa4fa21dccc418b6fb0e44ec8f5a1239810313b0e22c647dd |
| hovercard-subject-tag | issue:2984910367 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/VapiAI/example-server-javascript-node/6/issue_layout |
| twitter:image | https://opengraph.githubassets.com/3692d7c216b3501aa422032ce419016eaf8c71c659f0396547dfbcc679006b70/VapiAI/example-server-javascript-node/issues/6 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/3692d7c216b3501aa422032ce419016eaf8c71c659f0396547dfbcc679006b70/VapiAI/example-server-javascript-node/issues/6 |
| og:image:alt | Current Issue: I'm experiencing multiple identical tool calls being sent to my backend API during a single conversation. My backend receives repeated calls with the same parameters throughout a con... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | umerfarok |
| hostname | github.com |
| expected-hostname | github.com |
| None | da4f0ee56809799586f8ee546b27f94fe9b5893edfbf87732e82be45be013b52 |
| turbo-cache-control | no-preview |
| go-import | github.com/VapiAI/example-server-javascript-node git https://github.com/VapiAI/example-server-javascript-node.git |
| octolytics-dimension-user_id | 144965430 |
| octolytics-dimension-user_login | VapiAI |
| octolytics-dimension-repository_id | 753175131 |
| octolytics-dimension-repository_nwo | VapiAI/example-server-javascript-node |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 753175131 |
| octolytics-dimension-repository_network_root_nwo | VapiAI/example-server-javascript-node |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 847cd6771d7fb3caaa9384a1fe1215457fe1e4f4 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width