mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-17 07:55:32 +01:00
🛜 refactor: Streamline App Config Usage (#9234)
* WIP: app.locals refactoring
WIP: appConfig
fix: update memory configuration retrieval to use getAppConfig based on user role
fix: update comment for AppConfig interface to clarify purpose
🏷️ refactor: Update tests to use getAppConfig for endpoint configurations
ci: Update AppService tests to initialize app config instead of app.locals
ci: Integrate getAppConfig into remaining tests
refactor: Update multer storage destination to use promise-based getAppConfig and improve error handling in tests
refactor: Rename initializeAppConfig to setAppConfig and update related tests
ci: Mock getAppConfig in various tests to provide default configurations
refactor: Update convertMCPToolsToPlugins to use mcpManager for server configuration and adjust related tests
chore: rename `Config/getAppConfig` -> `Config/app`
fix: streamline OpenAI image tools configuration by removing direct appConfig dependency and using function parameters
chore: correct parameter documentation for imageOutputType in ToolService.js
refactor: remove `getCustomConfig` dependency in config route
refactor: update domain validation to use appConfig for allowed domains
refactor: use appConfig registration property
chore: remove app parameter from AppService invocation
refactor: update AppConfig interface to correct registration and turnstile configurations
refactor: remove getCustomConfig dependency and use getAppConfig in PluginController, multer, and MCP services
refactor: replace getCustomConfig with getAppConfig in STTService, TTSService, and related files
refactor: replace getCustomConfig with getAppConfig in Conversation and Message models, update tempChatRetention functions to use AppConfig type
refactor: update getAppConfig calls in Conversation and Message models to include user role for temporary chat expiration
ci: update related tests
refactor: update getAppConfig call in getCustomConfigSpeech to include user role
fix: update appConfig usage to access allowedDomains from actions instead of registration
refactor: enhance AppConfig to include fileStrategies and update related file strategy logic
refactor: update imports to use normalizeEndpointName from @librechat/api and remove redundant definitions
chore: remove deprecated unused RunManager
refactor: get balance config primarily from appConfig
refactor: remove customConfig dependency for appConfig and streamline loadConfigModels logic
refactor: remove getCustomConfig usage and use app config in file citations
refactor: consolidate endpoint loading logic into loadEndpoints function
refactor: update appConfig access to use endpoints structure across various services
refactor: implement custom endpoints configuration and streamline endpoint loading logic
refactor: update getAppConfig call to include user role parameter
refactor: streamline endpoint configuration and enhance appConfig usage across services
refactor: replace getMCPAuthMap with getUserMCPAuthMap and remove unused getCustomConfig file
refactor: add type annotation for loadedEndpoints in loadEndpoints function
refactor: move /services/Files/images/parse to TS API
chore: add missing FILE_CITATIONS permission to IRole interface
refactor: restructure toolkits to TS API
refactor: separate manifest logic into its own module
refactor: consolidate tool loading logic into a new tools module for startup logic
refactor: move interface config logic to TS API
refactor: migrate checkEmailConfig to TypeScript and update imports
refactor: add FunctionTool interface and availableTools to AppConfig
refactor: decouple caching and DB operations from AppService, make part of consolidated `getAppConfig`
WIP: fix tests
* fix: rebase conflicts
* refactor: remove app.locals references
* refactor: replace getBalanceConfig with getAppConfig in various strategies and middleware
* refactor: replace appConfig?.balance with getBalanceConfig in various controllers and clients
* test: add balance configuration to titleConvo method in AgentClient tests
* chore: remove unused `openai-chat-tokens` package
* chore: remove unused imports in initializeMCPs.js
* refactor: update balance configuration to use getAppConfig instead of getBalanceConfig
* refactor: integrate configMiddleware for centralized configuration handling
* refactor: optimize email domain validation by removing unnecessary async calls
* refactor: simplify multer storage configuration by removing async calls
* refactor: reorder imports for better readability in user.js
* refactor: replace getAppConfig calls with req.config for improved performance
* chore: replace getAppConfig calls with req.config in tests for centralized configuration handling
* chore: remove unused override config
* refactor: add configMiddleware to endpoint route and replace getAppConfig with req.config
* chore: remove customConfig parameter from TTSService constructor
* refactor: pass appConfig from request to processFileCitations for improved configuration handling
* refactor: remove configMiddleware from endpoint route and retrieve appConfig directly in getEndpointsConfig if not in `req.config`
* test: add mockAppConfig to processFileCitations tests for improved configuration handling
* fix: pass req.config to hasCustomUserVars and call without await after synchronous refactor
* fix: type safety in useExportConversation
* refactor: retrieve appConfig using getAppConfig in PluginController and remove configMiddleware from plugins route, to avoid always retrieving when plugins are cached
* chore: change `MongoUser` typedef to `IUser`
* fix: Add `user` and `config` fields to ServerRequest and update JSDoc type annotations from Express.Request to ServerRequest
* fix: remove unused setAppConfig mock from Server configuration tests
This commit is contained in:
parent
e1ad235f17
commit
9a210971f5
210 changed files with 4102 additions and 3465 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import { AuthType, Constants, EToolResources } from 'librechat-data-provider';
|
||||
import type { TPlugin, FunctionTool, TCustomConfig } from 'librechat-data-provider';
|
||||
import type { TPlugin, FunctionTool } from 'librechat-data-provider';
|
||||
import type { MCPManager } from '~/mcp/MCPManager';
|
||||
import {
|
||||
convertMCPToolsToPlugins,
|
||||
filterUniquePlugins,
|
||||
|
|
@ -277,19 +278,18 @@ describe('format.ts helper functions', () => {
|
|||
} as FunctionTool,
|
||||
};
|
||||
|
||||
const customConfig: Partial<TCustomConfig> = {
|
||||
mcpServers: {
|
||||
server1: {
|
||||
command: 'test',
|
||||
args: [],
|
||||
iconPath: '/path/to/icon.png',
|
||||
},
|
||||
},
|
||||
};
|
||||
const mockMcpManager = {
|
||||
getRawConfig: jest.fn().mockReturnValue({
|
||||
command: 'test',
|
||||
args: [],
|
||||
iconPath: '/path/to/icon.png',
|
||||
}),
|
||||
} as unknown as MCPManager;
|
||||
|
||||
const result = convertMCPToolsToPlugins({ functionTools, customConfig });
|
||||
const result = convertMCPToolsToPlugins({ functionTools, mcpManager: mockMcpManager });
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result![0].icon).toBe('/path/to/icon.png');
|
||||
expect(mockMcpManager.getRawConfig).toHaveBeenCalledWith('server1');
|
||||
});
|
||||
|
||||
it('should handle customUserVars in server config', () => {
|
||||
|
|
@ -300,26 +300,25 @@ describe('format.ts helper functions', () => {
|
|||
} as FunctionTool,
|
||||
};
|
||||
|
||||
const customConfig: Partial<TCustomConfig> = {
|
||||
mcpServers: {
|
||||
server1: {
|
||||
command: 'test',
|
||||
args: [],
|
||||
customUserVars: {
|
||||
API_KEY: { title: 'API Key', description: 'Your API key' },
|
||||
SECRET: { title: 'Secret', description: 'Your secret' },
|
||||
},
|
||||
const mockMcpManager = {
|
||||
getRawConfig: jest.fn().mockReturnValue({
|
||||
command: 'test',
|
||||
args: [],
|
||||
customUserVars: {
|
||||
API_KEY: { title: 'API Key', description: 'Your API key' },
|
||||
SECRET: { title: 'Secret', description: 'Your secret' },
|
||||
},
|
||||
},
|
||||
};
|
||||
}),
|
||||
} as unknown as MCPManager;
|
||||
|
||||
const result = convertMCPToolsToPlugins({ functionTools, customConfig });
|
||||
const result = convertMCPToolsToPlugins({ functionTools, mcpManager: mockMcpManager });
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result![0].authConfig).toHaveLength(2);
|
||||
expect(result![0].authConfig).toEqual([
|
||||
{ authField: 'API_KEY', label: 'API Key', description: 'Your API key' },
|
||||
{ authField: 'SECRET', label: 'Secret', description: 'Your secret' },
|
||||
]);
|
||||
expect(mockMcpManager.getRawConfig).toHaveBeenCalledWith('server1');
|
||||
});
|
||||
|
||||
it('should use key as label when title is missing in customUserVars', () => {
|
||||
|
|
@ -330,23 +329,22 @@ describe('format.ts helper functions', () => {
|
|||
} as FunctionTool,
|
||||
};
|
||||
|
||||
const customConfig: Partial<TCustomConfig> = {
|
||||
mcpServers: {
|
||||
server1: {
|
||||
command: 'test',
|
||||
args: [],
|
||||
customUserVars: {
|
||||
API_KEY: { title: 'API Key', description: 'Your API key' },
|
||||
},
|
||||
const mockMcpManager = {
|
||||
getRawConfig: jest.fn().mockReturnValue({
|
||||
command: 'test',
|
||||
args: [],
|
||||
customUserVars: {
|
||||
API_KEY: { title: 'API Key', description: 'Your API key' },
|
||||
},
|
||||
},
|
||||
};
|
||||
}),
|
||||
} as unknown as MCPManager;
|
||||
|
||||
const result = convertMCPToolsToPlugins({ functionTools, customConfig });
|
||||
const result = convertMCPToolsToPlugins({ functionTools, mcpManager: mockMcpManager });
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result![0].authConfig).toEqual([
|
||||
{ authField: 'API_KEY', label: 'API Key', description: 'Your API key' },
|
||||
]);
|
||||
expect(mockMcpManager.getRawConfig).toHaveBeenCalledWith('server1');
|
||||
});
|
||||
|
||||
it('should handle empty customUserVars', () => {
|
||||
|
|
@ -357,19 +355,51 @@ describe('format.ts helper functions', () => {
|
|||
} as FunctionTool,
|
||||
};
|
||||
|
||||
const customConfig: Partial<TCustomConfig> = {
|
||||
mcpServers: {
|
||||
server1: {
|
||||
command: 'test',
|
||||
args: [],
|
||||
customUserVars: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
const mockMcpManager = {
|
||||
getRawConfig: jest.fn().mockReturnValue({
|
||||
command: 'test',
|
||||
args: [],
|
||||
customUserVars: {},
|
||||
}),
|
||||
} as unknown as MCPManager;
|
||||
|
||||
const result = convertMCPToolsToPlugins({ functionTools, customConfig });
|
||||
const result = convertMCPToolsToPlugins({ functionTools, mcpManager: mockMcpManager });
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result![0].authConfig).toEqual([]);
|
||||
expect(mockMcpManager.getRawConfig).toHaveBeenCalledWith('server1');
|
||||
});
|
||||
|
||||
it('should handle missing mcpManager', () => {
|
||||
const functionTools: Record<string, FunctionTool> = {
|
||||
[`tool1${Constants.mcp_delimiter}server1`]: {
|
||||
type: 'function',
|
||||
function: { name: 'tool1', description: 'Tool 1' },
|
||||
} as FunctionTool,
|
||||
};
|
||||
|
||||
const result = convertMCPToolsToPlugins({ functionTools });
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result![0].icon).toBeUndefined();
|
||||
expect(result![0].authConfig).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle when getRawConfig returns undefined', () => {
|
||||
const functionTools: Record<string, FunctionTool> = {
|
||||
[`tool1${Constants.mcp_delimiter}server1`]: {
|
||||
type: 'function',
|
||||
function: { name: 'tool1', description: 'Tool 1' },
|
||||
} as FunctionTool,
|
||||
};
|
||||
|
||||
const mockMcpManager = {
|
||||
getRawConfig: jest.fn().mockReturnValue(undefined),
|
||||
} as unknown as MCPManager;
|
||||
|
||||
const result = convertMCPToolsToPlugins({ functionTools, mcpManager: mockMcpManager });
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result![0].icon).toBeUndefined();
|
||||
expect(result![0].authConfig).toEqual([]);
|
||||
expect(mockMcpManager.getRawConfig).toHaveBeenCalledWith('server1');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { AuthType, Constants, EToolResources } from 'librechat-data-provider';
|
||||
import type { TCustomConfig, TPlugin } from 'librechat-data-provider';
|
||||
import type { TPlugin } from 'librechat-data-provider';
|
||||
import type { MCPManager } from '~/mcp/MCPManager';
|
||||
import { LCAvailableTools, LCFunctionTool } from '~/mcp/types';
|
||||
|
||||
/**
|
||||
|
|
@ -58,11 +59,11 @@ export const checkPluginAuth = (plugin?: TPlugin): boolean => {
|
|||
export function convertMCPToolToPlugin({
|
||||
toolKey,
|
||||
toolData,
|
||||
customConfig,
|
||||
mcpManager,
|
||||
}: {
|
||||
toolKey: string;
|
||||
toolData: LCFunctionTool;
|
||||
customConfig?: Partial<TCustomConfig> | null;
|
||||
mcpManager?: MCPManager;
|
||||
}): TPlugin | undefined {
|
||||
if (!toolData.function || !toolKey.includes(Constants.mcp_delimiter)) {
|
||||
return;
|
||||
|
|
@ -72,7 +73,7 @@ export function convertMCPToolToPlugin({
|
|||
const parts = toolKey.split(Constants.mcp_delimiter);
|
||||
const serverName = parts[parts.length - 1];
|
||||
|
||||
const serverConfig = customConfig?.mcpServers?.[serverName];
|
||||
const serverConfig = mcpManager?.getRawConfig(serverName);
|
||||
|
||||
const plugin: TPlugin = {
|
||||
/** Tool name without server suffix */
|
||||
|
|
@ -111,10 +112,10 @@ export function convertMCPToolToPlugin({
|
|||
*/
|
||||
export function convertMCPToolsToPlugins({
|
||||
functionTools,
|
||||
customConfig,
|
||||
mcpManager,
|
||||
}: {
|
||||
functionTools?: LCAvailableTools;
|
||||
customConfig?: Partial<TCustomConfig> | null;
|
||||
mcpManager?: MCPManager;
|
||||
}): TPlugin[] | undefined {
|
||||
if (!functionTools || typeof functionTools !== 'object') {
|
||||
return;
|
||||
|
|
@ -122,7 +123,7 @@ export function convertMCPToolsToPlugins({
|
|||
|
||||
const plugins: TPlugin[] = [];
|
||||
for (const [toolKey, toolData] of Object.entries(functionTools)) {
|
||||
const plugin = convertMCPToolToPlugin({ toolKey, toolData, customConfig });
|
||||
const plugin = convertMCPToolToPlugin({ toolKey, toolData, mcpManager });
|
||||
if (plugin) {
|
||||
plugins.push(plugin);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
export * from './format';
|
||||
export * from './toolkits';
|
||||
|
|
|
|||
2
packages/api/src/tools/toolkits/index.ts
Normal file
2
packages/api/src/tools/toolkits/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export * from './oai';
|
||||
export * from './yt';
|
||||
153
packages/api/src/tools/toolkits/oai.ts
Normal file
153
packages/api/src/tools/toolkits/oai.ts
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
/** Default descriptions for image generation tool */
|
||||
const DEFAULT_IMAGE_GEN_DESCRIPTION =
|
||||
`Generates high-quality, original images based solely on text, not using any uploaded reference images.
|
||||
|
||||
When to use \`image_gen_oai\`:
|
||||
- To create entirely new images from detailed text descriptions that do NOT reference any image files.
|
||||
|
||||
When NOT to use \`image_gen_oai\`:
|
||||
- If the user has uploaded any images and requests modifications, enhancements, or remixing based on those uploads → use \`image_edit_oai\` instead.
|
||||
|
||||
Generated image IDs will be returned in the response, so you can refer to them in future requests made to \`image_edit_oai\`.` as const;
|
||||
|
||||
const getImageGenDescription = () => {
|
||||
return process.env.IMAGE_GEN_OAI_DESCRIPTION || DEFAULT_IMAGE_GEN_DESCRIPTION;
|
||||
};
|
||||
|
||||
/** Default prompt descriptions */
|
||||
const DEFAULT_IMAGE_GEN_PROMPT_DESCRIPTION = `Describe the image you want in detail.
|
||||
Be highly specific—break your idea into layers:
|
||||
(1) main concept and subject,
|
||||
(2) composition and position,
|
||||
(3) lighting and mood,
|
||||
(4) style, medium, or camera details,
|
||||
(5) important features (age, expression, clothing, etc.),
|
||||
(6) background.
|
||||
Use positive, descriptive language and specify what should be included, not what to avoid.
|
||||
List number and characteristics of people/objects, and mention style/technical requirements (e.g., "DSLR photo, 85mm lens, golden hour").
|
||||
Do not reference any uploaded images—use for new image creation from text only.` as const;
|
||||
|
||||
const getImageGenPromptDescription = () => {
|
||||
return process.env.IMAGE_GEN_OAI_PROMPT_DESCRIPTION || DEFAULT_IMAGE_GEN_PROMPT_DESCRIPTION;
|
||||
};
|
||||
|
||||
/** Default description for image editing tool */
|
||||
const DEFAULT_IMAGE_EDIT_DESCRIPTION =
|
||||
`Generates high-quality, original images based on text and one or more uploaded/referenced images.
|
||||
|
||||
When to use \`image_edit_oai\`:
|
||||
- The user wants to modify, extend, or remix one **or more** uploaded images, either:
|
||||
- Previously generated, or in the current request (both to be included in the \`image_ids\` array).
|
||||
- Always when the user refers to uploaded images for editing, enhancement, remixing, style transfer, or combining elements.
|
||||
- Any current or existing images are to be used as visual guides.
|
||||
- If there are any files in the current request, they are more likely than not expected as references for image edit requests.
|
||||
|
||||
When NOT to use \`image_edit_oai\`:
|
||||
- Brand-new generations that do not rely on an existing image → use \`image_gen_oai\` instead.
|
||||
|
||||
Both generated and referenced image IDs will be returned in the response, so you can refer to them in future requests made to \`image_edit_oai\`.
|
||||
`.trim();
|
||||
|
||||
const getImageEditDescription = () => {
|
||||
return process.env.IMAGE_EDIT_OAI_DESCRIPTION || DEFAULT_IMAGE_EDIT_DESCRIPTION;
|
||||
};
|
||||
|
||||
const DEFAULT_IMAGE_EDIT_PROMPT_DESCRIPTION = `Describe the changes, enhancements, or new ideas to apply to the uploaded image(s).
|
||||
Be highly specific—break your request into layers:
|
||||
(1) main concept or transformation,
|
||||
(2) specific edits/replacements or composition guidance,
|
||||
(3) desired style, mood, or technique,
|
||||
(4) features/items to keep, change, or add (such as objects, people, clothing, lighting, etc.).
|
||||
Use positive, descriptive language and clarify what should be included or changed, not what to avoid.
|
||||
Always base this prompt on the most recently uploaded reference images.`;
|
||||
|
||||
const getImageEditPromptDescription = () => {
|
||||
return process.env.IMAGE_EDIT_OAI_PROMPT_DESCRIPTION || DEFAULT_IMAGE_EDIT_PROMPT_DESCRIPTION;
|
||||
};
|
||||
|
||||
export const oaiToolkit = {
|
||||
image_gen_oai: {
|
||||
name: 'image_gen_oai' as const,
|
||||
description: getImageGenDescription(),
|
||||
schema: z.object({
|
||||
prompt: z.string().max(32000).describe(getImageGenPromptDescription()),
|
||||
background: z
|
||||
.enum(['transparent', 'opaque', 'auto'])
|
||||
.optional()
|
||||
.describe(
|
||||
'Sets transparency for the background. Must be one of transparent, opaque or auto (default). When transparent, the output format should be png or webp.',
|
||||
),
|
||||
/*
|
||||
n: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(10)
|
||||
.optional()
|
||||
.describe('The number of images to generate. Must be between 1 and 10.'),
|
||||
output_compression: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.max(100)
|
||||
.optional()
|
||||
.describe('The compression level (0-100%) for webp or jpeg formats. Defaults to 100.'),
|
||||
*/
|
||||
quality: z
|
||||
.enum(['auto', 'high', 'medium', 'low'])
|
||||
.optional()
|
||||
.describe('The quality of the image. One of auto (default), high, medium, or low.'),
|
||||
size: z
|
||||
.enum(['auto', '1024x1024', '1536x1024', '1024x1536'])
|
||||
.optional()
|
||||
.describe(
|
||||
'The size of the generated image. One of 1024x1024, 1536x1024 (landscape), 1024x1536 (portrait), or auto (default).',
|
||||
),
|
||||
}),
|
||||
responseFormat: 'content_and_artifact' as const,
|
||||
} as const,
|
||||
image_edit_oai: {
|
||||
name: 'image_edit_oai' as const,
|
||||
description: getImageEditDescription(),
|
||||
schema: z.object({
|
||||
image_ids: z
|
||||
.array(z.string())
|
||||
.min(1)
|
||||
.describe(
|
||||
`
|
||||
IDs (image ID strings) of previously generated or uploaded images that should guide the edit.
|
||||
|
||||
Guidelines:
|
||||
- If the user's request depends on any prior image(s), copy their image IDs into the \`image_ids\` array (in the same order the user refers to them).
|
||||
- Never invent or hallucinate IDs; only use IDs that are still visible in the conversation context.
|
||||
- If no earlier image is relevant, omit the field entirely.
|
||||
`.trim(),
|
||||
),
|
||||
prompt: z.string().max(32000).describe(getImageEditPromptDescription()),
|
||||
/*
|
||||
n: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(10)
|
||||
.optional()
|
||||
.describe('The number of images to generate. Must be between 1 and 10. Defaults to 1.'),
|
||||
*/
|
||||
quality: z
|
||||
.enum(['auto', 'high', 'medium', 'low'])
|
||||
.optional()
|
||||
.describe(
|
||||
'The quality of the image. One of auto (default), high, medium, or low. High/medium/low only supported for gpt-image-1.',
|
||||
),
|
||||
size: z
|
||||
.enum(['auto', '1024x1024', '1536x1024', '1024x1536', '256x256', '512x512'])
|
||||
.optional()
|
||||
.describe(
|
||||
'The size of the generated images. For gpt-image-1: auto (default), 1024x1024, 1536x1024, 1024x1536. For dall-e-2: 256x256, 512x512, 1024x1024.',
|
||||
),
|
||||
}),
|
||||
responseFormat: 'content_and_artifact' as const,
|
||||
},
|
||||
} as const;
|
||||
61
packages/api/src/tools/toolkits/yt.ts
Normal file
61
packages/api/src/tools/toolkits/yt.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import { z } from 'zod';
|
||||
export const ytToolkit = {
|
||||
youtube_search: {
|
||||
name: 'youtube_search' as const,
|
||||
description: `Search for YouTube videos by keyword or phrase.
|
||||
- Required: query (search terms to find videos)
|
||||
- Optional: maxResults (number of videos to return, 1-50, default: 5)
|
||||
- Returns: List of videos with titles, descriptions, and URLs
|
||||
- Use for: Finding specific videos, exploring content, research
|
||||
Example: query="cooking pasta tutorials" maxResults=3` as const,
|
||||
schema: z.object({
|
||||
query: z.string().describe('Search query terms'),
|
||||
maxResults: z.number().int().min(1).max(50).optional().describe('Number of results (1-50)'),
|
||||
}),
|
||||
},
|
||||
youtube_info: {
|
||||
name: 'youtube_info' as const,
|
||||
description: `Get detailed metadata and statistics for a specific YouTube video.
|
||||
- Required: url (full YouTube URL or video ID)
|
||||
- Returns: Video title, description, view count, like count, comment count
|
||||
- Use for: Getting video metrics and basic metadata
|
||||
- DO NOT USE FOR VIDEO SUMMARIES, USE TRANSCRIPTS FOR COMPREHENSIVE ANALYSIS
|
||||
- Accepts both full URLs and video IDs
|
||||
Example: url="https://youtube.com/watch?v=abc123" or url="abc123"` as const,
|
||||
schema: z.object({
|
||||
url: z.string().describe('YouTube video URL or ID'),
|
||||
}),
|
||||
} as const,
|
||||
youtube_comments: {
|
||||
name: 'youtube_comments',
|
||||
description: `Retrieve top-level comments from a YouTube video.
|
||||
- Required: url (full YouTube URL or video ID)
|
||||
- Optional: maxResults (number of comments, 1-50, default: 10)
|
||||
- Returns: Comment text, author names, like counts
|
||||
- Use for: Sentiment analysis, audience feedback, engagement review
|
||||
Example: url="abc123" maxResults=20`,
|
||||
schema: z.object({
|
||||
url: z.string().describe('YouTube video URL or ID'),
|
||||
maxResults: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(50)
|
||||
.optional()
|
||||
.describe('Number of comments to retrieve'),
|
||||
}),
|
||||
} as const,
|
||||
youtube_transcript: {
|
||||
name: 'youtube_transcript',
|
||||
description: `Fetch and parse the transcript/captions of a YouTube video.
|
||||
- Required: url (full YouTube URL or video ID)
|
||||
- Returns: Full video transcript as plain text
|
||||
- Use for: Content analysis, summarization, translation reference
|
||||
- This is the "Go-to" tool for analyzing actual video content
|
||||
- Attempts to fetch English first, then German, then any available language
|
||||
Example: url="https://youtube.com/watch?v=abc123"`,
|
||||
schema: z.object({
|
||||
url: z.string().describe('YouTube video URL or ID'),
|
||||
}),
|
||||
} as const,
|
||||
} as const;
|
||||
Loading…
Add table
Add a link
Reference in a new issue