UNPKG

@copilotkit/runtime

Version:

<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />

146 lines (144 loc) 4.75 kB
require("reflect-metadata"); const require_runtime = require('../../../../_virtual/_rolldown/runtime.cjs'); const require_agent_utils = require('../shared/agent-utils.cjs'); const require_json_response = require('../shared/json-response.cjs'); let _copilotkit_shared = require("@copilotkit/shared"); let node_crypto = require("node:crypto"); //#region src/v2/runtime/handlers/intelligence/thread-names.ts const THREAD_NAME_SYSTEM_PROMPT = [ "You generate short, specific conversation titles.", "Return JSON only in this exact shape: {\"title\":\"...\"}", "The title must be 2 to 5 words.", "Use sentence case.", "No quotes.", "No emoji.", "No markdown characters or formatting.", "Do not use *, _, #, `, [, ], (, ), !, ~, >, or |.", "No trailing punctuation.", "No explanations.", "Do not call tools." ].join("\n"); const MAX_TITLE_LENGTH = 80; const MAX_TITLE_WORDS = 8; const MAX_TRANSCRIPT_MESSAGES = 8; const MAX_TITLE_GENERATION_ATTEMPTS = 3; const FALLBACK_THREAD_TITLE = "Untitled"; async function generateThreadNameForNewThread({ runtime, request, agentId, sourceInput, thread, userId }) { if (!runtime.generateThreadNames || hasThreadName(thread.name)) return; const prompt = buildThreadTitlePrompt(sourceInput.messages); if (!prompt) return; let generatedTitle = null; for (let attempt = 1; attempt <= MAX_TITLE_GENERATION_ATTEMPTS; attempt++) try { generatedTitle = await runTitleGenerationAttempt({ runtime, request, agentId, threadId: thread.id, prompt }); if (generatedTitle) break; _copilotkit_shared.logger.warn({ agentId, attempt, threadId: thread.id }, "Thread name generation returned an empty or invalid title"); } catch (error) { _copilotkit_shared.logger.warn({ err: error, agentId, attempt, threadId: thread.id }, "Thread name generation attempt failed"); } await runtime.intelligence.updateThread({ threadId: thread.id, userId, agentId, updates: { name: generatedTitle ?? FALLBACK_THREAD_TITLE } }); } async function runTitleGenerationAttempt(params) { const { runtime, request, agentId, threadId, prompt } = params; const agent = await require_agent_utils.cloneAgentForRequest(runtime, agentId, request); if (require_json_response.isHandlerResponse(agent)) { _copilotkit_shared.logger.warn({ agentId, threadId }, "Skipping thread naming because the agent could not be cloned"); return null; } require_agent_utils.configureAgentForRequest({ runtime, request, agentId, agent }); const messages = [{ id: (0, node_crypto.randomUUID)(), role: "system", content: THREAD_NAME_SYSTEM_PROMPT }, { id: (0, node_crypto.randomUUID)(), role: "user", content: prompt }]; agent.setMessages(messages); agent.setState({}); agent.threadId = (0, node_crypto.randomUUID)(); const { newMessages } = await agent.runAgent({ messages, state: {}, tools: [], context: [], forwardedProps: {} }); const lastMessage = newMessages.at(-1); return normalizeGeneratedTitle(lastMessage ? stringifyMessageContent(lastMessage.content) : ""); } function buildThreadTitlePrompt(messages) { const transcript = (messages ?? []).filter((message) => [ "user", "assistant", "system", "developer" ].includes(message.role)).map((message) => { const content = stringifyMessageContent(message.content); if (!content) return null; return `${message.role}: ${content}`; }).filter((message) => !!message).slice(-MAX_TRANSCRIPT_MESSAGES); if (transcript.length === 0) return null; return [ "Generate a short title for this conversation.", "Conversation:", transcript.join("\n") ].join("\n\n"); } function stringifyMessageContent(content) { if (typeof content === "string") return content.trim(); if (content == null) return ""; try { return JSON.stringify(content).trim(); } catch { return ""; } } function normalizeGeneratedTitle(rawTitle) { let candidate = rawTitle.trim(); if (!candidate) return null; candidate = candidate.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "").trim(); try { const parsed = JSON.parse(candidate); if (typeof parsed.title === "string") candidate = parsed.title; } catch {} candidate = candidate.replace(/^["'`]+|["'`]+$/g, "").replace(/[*_#[\]()!~>|]+/g, "").replace(/[.!?,;:]+$/g, "").replace(/\s+/g, " ").trim(); if (!candidate) return null; if (candidate.length > MAX_TITLE_LENGTH) candidate = candidate.slice(0, MAX_TITLE_LENGTH).trim(); if (candidate.split(/\s+/).length > MAX_TITLE_WORDS) return null; return candidate; } function hasThreadName(name) { return typeof name === "string" && name.trim().length > 0; } //#endregion exports.generateThreadNameForNewThread = generateThreadNameForNewThread; //# sourceMappingURL=thread-names.cjs.map