mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-10-21 01:22:54 +00:00
🗂️ feat: Add Deployment Skill Directory (#13523)
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions
* feat: Add deployment skill directory * chore: Address deployment skill review feedback * fix: Include deployment skill file metadata * test: Add deployment skills e2e smoke test
This commit is contained in:
parent
6357ea10c1
commit
2c8d54e18c
32 changed files with 1836 additions and 69 deletions
|
|
@ -5,6 +5,8 @@ const {
|
|||
toSkillStatesRecord,
|
||||
validateSkillStatesPayload,
|
||||
pruneOrphanSkillStates,
|
||||
getDeploymentSkillIds,
|
||||
mergeDeploymentSkillIds,
|
||||
} = require('@librechat/api');
|
||||
const { ResourceType, PermissionBits } = require('librechat-data-provider');
|
||||
const { findAccessibleResources } = require('~/server/services/PermissionService');
|
||||
|
|
@ -21,15 +23,20 @@ function buildPruneDeps(user) {
|
|||
const existing = await Skill.find({ _id: { $in: validIds } })
|
||||
.select('_id')
|
||||
.lean();
|
||||
return existing.map((doc) => doc._id.toString());
|
||||
const deploymentIds = getDeploymentSkillIds()
|
||||
.map((id) => id.toString())
|
||||
.filter((id) => validIds.includes(id));
|
||||
return [...existing.map((doc) => doc._id.toString()), ...deploymentIds];
|
||||
},
|
||||
findAccessibleSkillIds: () =>
|
||||
findAccessibleResources({
|
||||
userId: user.id,
|
||||
role: user.role,
|
||||
resourceType: ResourceType.SKILL,
|
||||
requiredPermissions: PermissionBits.VIEW,
|
||||
}),
|
||||
findAccessibleSkillIds: async () =>
|
||||
mergeDeploymentSkillIds(
|
||||
await findAccessibleResources({
|
||||
userId: user.id,
|
||||
role: user.role,
|
||||
resourceType: ResourceType.SKILL,
|
||||
requiredPermissions: PermissionBits.VIEW,
|
||||
}),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -150,7 +150,9 @@ jest.mock('~/server/services/Files/permissions', () => ({
|
|||
|
||||
jest.mock('~/server/services/Endpoints/agents/skillDeps', () => ({
|
||||
getSkillToolDeps: mockGetSkillToolDeps,
|
||||
getSkillDbMethods: jest.fn(() => ({})),
|
||||
canAuthorSkillFiles: mockCanAuthorSkillFiles,
|
||||
withDeploymentSkillIds: jest.fn((ids = []) => ids),
|
||||
enrichWithSkillConfigurable: mockEnrichWithSkillConfigurable,
|
||||
buildSkillPrimedIdsByName: mockBuildSkillPrimedIdsByName,
|
||||
buildAgentToolContext: mockBuildAgentToolContext,
|
||||
|
|
|
|||
|
|
@ -200,7 +200,9 @@ jest.mock('~/server/services/Files/permissions', () => ({
|
|||
|
||||
jest.mock('~/server/services/Endpoints/agents/skillDeps', () => ({
|
||||
getSkillToolDeps: mockGetSkillToolDeps,
|
||||
getSkillDbMethods: jest.fn(() => ({})),
|
||||
canAuthorSkillFiles: mockCanAuthorSkillFiles,
|
||||
withDeploymentSkillIds: jest.fn((ids = []) => ids),
|
||||
enrichWithSkillConfigurable: mockEnrichWithSkillConfigurable,
|
||||
buildSkillPrimedIdsByName: mockBuildSkillPrimedIdsByName,
|
||||
buildAgentToolContext: mockBuildAgentToolContext,
|
||||
|
|
|
|||
|
|
@ -47,7 +47,9 @@ const {
|
|||
} = require('~/server/services/PermissionService');
|
||||
const {
|
||||
getSkillToolDeps,
|
||||
getSkillDbMethods,
|
||||
canAuthorSkillFiles,
|
||||
withDeploymentSkillIds,
|
||||
buildAgentToolContext,
|
||||
enrichLoadedToolsWithAgentContext,
|
||||
} = require('~/server/services/Endpoints/agents/skillDeps');
|
||||
|
|
@ -229,6 +231,7 @@ const OpenAIChatCompletionController = async (req, res) => {
|
|||
endpoint: agent.provider,
|
||||
model_parameters: agent.model_parameters ?? {},
|
||||
};
|
||||
const skillDbMethods = getSkillDbMethods();
|
||||
|
||||
// `filterFilesByAgentAccess` is intentionally omitted: it calls
|
||||
// `checkPermission` with `resourceType: AGENT`, but this route
|
||||
|
|
@ -246,21 +249,23 @@ const OpenAIChatCompletionController = async (req, res) => {
|
|||
getUserCodeFiles: db.getUserCodeFiles,
|
||||
getToolFilesByIds: db.getToolFilesByIds,
|
||||
getCodeGeneratedFiles: db.getCodeGeneratedFiles,
|
||||
listSkillsByAccess: db.listSkillsByAccess,
|
||||
listAlwaysApplySkills: db.listAlwaysApplySkills,
|
||||
getSkillByName: db.getSkillByName,
|
||||
listSkillsByAccess: skillDbMethods.listSkillsByAccess,
|
||||
listAlwaysApplySkills: skillDbMethods.listAlwaysApplySkills,
|
||||
getSkillByName: skillDbMethods.getSkillByName,
|
||||
};
|
||||
|
||||
const enabledCapabilities = new Set(agentsEConfig?.capabilities);
|
||||
const skillsCapabilityEnabled = enabledCapabilities.has(AgentCapabilities.skills);
|
||||
const ephemeralSkillsToggle = req.body?.ephemeralAgent?.skills === true;
|
||||
const accessibleSkillIds = skillsCapabilityEnabled
|
||||
? await findAccessibleResources({
|
||||
userId: req.user.id,
|
||||
role: req.user.role,
|
||||
resourceType: ResourceType.SKILL,
|
||||
requiredPermissions: PermissionBits.VIEW,
|
||||
})
|
||||
? withDeploymentSkillIds(
|
||||
await findAccessibleResources({
|
||||
userId: req.user.id,
|
||||
role: req.user.role,
|
||||
resourceType: ResourceType.SKILL,
|
||||
requiredPermissions: PermissionBits.VIEW,
|
||||
}),
|
||||
)
|
||||
: [];
|
||||
const editableSkillIds = skillsCapabilityEnabled
|
||||
? await findAccessibleResources({
|
||||
|
|
|
|||
|
|
@ -56,7 +56,9 @@ const {
|
|||
} = require('~/server/services/PermissionService');
|
||||
const {
|
||||
getSkillToolDeps,
|
||||
getSkillDbMethods,
|
||||
canAuthorSkillFiles,
|
||||
withDeploymentSkillIds,
|
||||
buildAgentToolContext,
|
||||
enrichLoadedToolsWithAgentContext,
|
||||
} = require('~/server/services/Endpoints/agents/skillDeps');
|
||||
|
|
@ -351,6 +353,7 @@ const createResponse = async (req, res) => {
|
|||
|
||||
// Create tool loader
|
||||
const loadTools = createToolLoader(abortController.signal);
|
||||
const skillDbMethods = getSkillDbMethods();
|
||||
|
||||
// Initialize the agent first to check for disableStreaming
|
||||
const endpointOption = {
|
||||
|
|
@ -374,9 +377,9 @@ const createResponse = async (req, res) => {
|
|||
getUserCodeFiles: db.getUserCodeFiles,
|
||||
getToolFilesByIds: db.getToolFilesByIds,
|
||||
getCodeGeneratedFiles: db.getCodeGeneratedFiles,
|
||||
listSkillsByAccess: db.listSkillsByAccess,
|
||||
listAlwaysApplySkills: db.listAlwaysApplySkills,
|
||||
getSkillByName: db.getSkillByName,
|
||||
listSkillsByAccess: skillDbMethods.listSkillsByAccess,
|
||||
listAlwaysApplySkills: skillDbMethods.listAlwaysApplySkills,
|
||||
getSkillByName: skillDbMethods.getSkillByName,
|
||||
};
|
||||
|
||||
const enabledCapabilities = new Set(
|
||||
|
|
@ -385,12 +388,14 @@ const createResponse = async (req, res) => {
|
|||
const skillsCapabilityEnabled = enabledCapabilities.has(AgentCapabilities.skills);
|
||||
const ephemeralSkillsToggle = req.body?.ephemeralAgent?.skills === true;
|
||||
const accessibleSkillIds = skillsCapabilityEnabled
|
||||
? await findAccessibleResources({
|
||||
userId: req.user.id,
|
||||
role: req.user.role,
|
||||
resourceType: ResourceType.SKILL,
|
||||
requiredPermissions: PermissionBits.VIEW,
|
||||
})
|
||||
? withDeploymentSkillIds(
|
||||
await findAccessibleResources({
|
||||
userId: req.user.id,
|
||||
role: req.user.role,
|
||||
resourceType: ResourceType.SKILL,
|
||||
requiredPermissions: PermissionBits.VIEW,
|
||||
}),
|
||||
)
|
||||
: [];
|
||||
const editableSkillIds = skillsCapabilityEnabled
|
||||
? await findAccessibleResources({
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ const {
|
|||
GenerationJobManager,
|
||||
createStreamServices,
|
||||
initializeFileStorage,
|
||||
initializeDeploymentSkills,
|
||||
preAuthTenantMiddleware,
|
||||
setupGracefulShutdown,
|
||||
updateInterfacePermissions,
|
||||
|
|
@ -116,6 +117,7 @@ const startServer = async () => {
|
|||
});
|
||||
const appConfig = await getAppConfig({ baseOnly: true });
|
||||
initializeFileStorage(appConfig);
|
||||
await initializeDeploymentSkills({ projectRoot: path.resolve(__dirname, '../..') });
|
||||
startExpiredFileSweep({ appConfig, loadAppConfig: getAppConfig });
|
||||
await runAsSystem(async () => {
|
||||
await performStartupChecks(appConfig);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
const { ResourceType } = require('librechat-data-provider');
|
||||
const { ResourceType, PermissionBits } = require('librechat-data-provider');
|
||||
const { canAccessResource } = require('./canAccessResource');
|
||||
const { getSkillById } = require('~/models');
|
||||
const { getDeploymentSkillById } = require('@librechat/api');
|
||||
|
||||
/**
|
||||
* Skill-specific middleware factory that checks skill access permissions.
|
||||
|
|
@ -19,12 +20,35 @@ const canAccessSkillResource = (options) => {
|
|||
throw new Error('canAccessSkillResource: requiredPermission is required and must be a number');
|
||||
}
|
||||
|
||||
return canAccessResource({
|
||||
const aclMiddleware = canAccessResource({
|
||||
resourceType: ResourceType.SKILL,
|
||||
requiredPermission,
|
||||
resourceIdParam,
|
||||
idResolver: getSkillById,
|
||||
});
|
||||
|
||||
return (req, res, next) => {
|
||||
const rawResourceId = req.params[resourceIdParam];
|
||||
const deploymentSkill = rawResourceId ? getDeploymentSkillById(rawResourceId) : null;
|
||||
if (!deploymentSkill) {
|
||||
return aclMiddleware(req, res, next);
|
||||
}
|
||||
if (requiredPermission !== PermissionBits.VIEW) {
|
||||
return res.status(403).json({
|
||||
error: 'Forbidden',
|
||||
message: 'Deployment skills are read-only',
|
||||
});
|
||||
}
|
||||
req.resourceAccess = {
|
||||
resourceType: ResourceType.SKILL,
|
||||
resourceId: deploymentSkill._id,
|
||||
customResourceId: rawResourceId,
|
||||
permission: requiredPermission,
|
||||
userId: req.user?.id,
|
||||
resourceInfo: deploymentSkill,
|
||||
};
|
||||
return next();
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
|
|
|||
|
|
@ -21,14 +21,11 @@ const {
|
|||
const {
|
||||
createSkill,
|
||||
getSkillById,
|
||||
listSkillsByAccess,
|
||||
updateSkill,
|
||||
deleteSkill,
|
||||
listSkillFiles,
|
||||
upsertSkillFile,
|
||||
deleteSkillFile,
|
||||
getSkillFileByPath,
|
||||
updateSkillFileContent,
|
||||
getRoleByName,
|
||||
} = require('~/models');
|
||||
const { requireJwtAuth, canAccessSkillResource } = require('~/server/middleware');
|
||||
|
|
@ -42,6 +39,11 @@ const { getStrategyFunctions } = require('~/server/services/Files/strategies');
|
|||
const { createFileLimiters } = require('~/server/middleware/limiters/uploadLimiters');
|
||||
const configMiddleware = require('~/server/middleware/config/app');
|
||||
const { getFileStrategy } = require('~/server/utils/getFileStrategy');
|
||||
const {
|
||||
getSkillDbMethods,
|
||||
withDeploymentSkillIds,
|
||||
getSkillStrategyFunctions,
|
||||
} = require('~/server/services/Endpoints/agents/skillDeps');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
|
|
@ -100,6 +102,7 @@ const checkSkillCreate = generateCheckAccess({
|
|||
// Rate limiters (reuse existing file upload limiters)
|
||||
// ---------------------------------------------------------------------------
|
||||
const { fileUploadIpLimiter, fileUploadUserLimiter } = createFileLimiters();
|
||||
const skillDbMethods = getSkillDbMethods();
|
||||
|
||||
router.use(requireJwtAuth);
|
||||
router.use(configMiddleware);
|
||||
|
|
@ -110,18 +113,28 @@ router.use(checkSkillAccess);
|
|||
// ---------------------------------------------------------------------------
|
||||
const handlers = createSkillsHandlers({
|
||||
createSkill,
|
||||
getSkillById,
|
||||
listSkillsByAccess,
|
||||
getSkillById: skillDbMethods.getSkillById,
|
||||
listSkillsByAccess: skillDbMethods.listSkillsByAccess,
|
||||
updateSkill,
|
||||
deleteSkill,
|
||||
listSkillFiles,
|
||||
listSkillFiles: skillDbMethods.listSkillFiles,
|
||||
deleteSkillFile,
|
||||
getSkillFileByPath,
|
||||
updateSkillFileContent,
|
||||
getStrategyFunctions,
|
||||
findAccessibleResources,
|
||||
findPubliclyAccessibleResources,
|
||||
hasPublicPermission,
|
||||
getSkillFileByPath: skillDbMethods.getSkillFileByPath,
|
||||
updateSkillFileContent: skillDbMethods.updateSkillFileContent,
|
||||
getStrategyFunctions: getSkillStrategyFunctions,
|
||||
findAccessibleResources: async (params) =>
|
||||
params.resourceType === 'skill' && params.requiredPermissions === PermissionBits.VIEW
|
||||
? withDeploymentSkillIds(await findAccessibleResources(params))
|
||||
: findAccessibleResources(params),
|
||||
findPubliclyAccessibleResources: async (params) =>
|
||||
params.resourceType === 'skill' && params.requiredPermissions === PermissionBits.VIEW
|
||||
? withDeploymentSkillIds(await findPubliclyAccessibleResources(params))
|
||||
: findPubliclyAccessibleResources(params),
|
||||
hasPublicPermission: async (params) =>
|
||||
params.resourceType === 'skill' && params.requiredPermissions === PermissionBits.VIEW
|
||||
? withDeploymentSkillIds([]).some((id) => id.toString() === params.resourceId.toString()) ||
|
||||
hasPublicPermission(params)
|
||||
: hasPublicPermission(params),
|
||||
grantPermission,
|
||||
isValidObjectIdString,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ const { loadAgentTools, loadToolsForExecution } = require('~/server/services/Too
|
|||
const { filterFilesByAgentAccess } = require('~/server/services/Files/permissions');
|
||||
const {
|
||||
getSkillToolDeps,
|
||||
getSkillDbMethods,
|
||||
canAuthorSkillFiles,
|
||||
withDeploymentSkillIds,
|
||||
buildAgentToolContext,
|
||||
enrichLoadedToolsWithAgentContext,
|
||||
} = require('./skillDeps');
|
||||
|
|
@ -143,14 +145,17 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
|
|||
const skillsCapabilityEnabled = enabledCapabilities.has(AgentCapabilities.skills);
|
||||
const codeEnvAvailable = enabledCapabilities.has(AgentCapabilities.execute_code);
|
||||
const ephemeralSkillsToggle = req.body?.ephemeralAgent?.skills === true;
|
||||
const skillDbMethods = getSkillDbMethods();
|
||||
|
||||
const accessibleSkillIds = skillsCapabilityEnabled
|
||||
? await findAccessibleResources({
|
||||
userId: req.user.id,
|
||||
role: req.user.role,
|
||||
resourceType: ResourceType.SKILL,
|
||||
requiredPermissions: PermissionBits.VIEW,
|
||||
})
|
||||
? withDeploymentSkillIds(
|
||||
await findAccessibleResources({
|
||||
userId: req.user.id,
|
||||
role: req.user.role,
|
||||
resourceType: ResourceType.SKILL,
|
||||
requiredPermissions: PermissionBits.VIEW,
|
||||
}),
|
||||
)
|
||||
: [];
|
||||
const editableSkillIds = skillsCapabilityEnabled
|
||||
? await findAccessibleResources({
|
||||
|
|
@ -368,9 +373,9 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
|
|||
getToolFilesByIds: db.getToolFilesByIds,
|
||||
getCodeGeneratedFiles: db.getCodeGeneratedFiles,
|
||||
filterFilesByAgentAccess,
|
||||
listSkillsByAccess: db.listSkillsByAccess,
|
||||
listAlwaysApplySkills: db.listAlwaysApplySkills,
|
||||
getSkillByName: db.getSkillByName,
|
||||
listSkillsByAccess: skillDbMethods.listSkillsByAccess,
|
||||
listAlwaysApplySkills: skillDbMethods.listAlwaysApplySkills,
|
||||
getSkillByName: skillDbMethods.getSkillByName,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -439,9 +444,9 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
|
|||
getToolFilesByIds: db.getToolFilesByIds,
|
||||
getCodeGeneratedFiles: db.getCodeGeneratedFiles,
|
||||
filterFilesByAgentAccess,
|
||||
listSkillsByAccess: db.listSkillsByAccess,
|
||||
listAlwaysApplySkills: db.listAlwaysApplySkills,
|
||||
getSkillByName: db.getSkillByName,
|
||||
listSkillsByAccess: skillDbMethods.listSkillsByAccess,
|
||||
listAlwaysApplySkills: skillDbMethods.listAlwaysApplySkills,
|
||||
getSkillByName: skillDbMethods.getSkillByName,
|
||||
},
|
||||
// The callback fires during BFS, before the helper prunes agents
|
||||
// whose edges end up filtered. Don't populate `agentConfigs` here —
|
||||
|
|
@ -646,9 +651,9 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => {
|
|||
getToolFilesByIds: db.getToolFilesByIds,
|
||||
getCodeGeneratedFiles: db.getCodeGeneratedFiles,
|
||||
filterFilesByAgentAccess,
|
||||
listSkillsByAccess: db.listSkillsByAccess,
|
||||
listAlwaysApplySkills: db.listAlwaysApplySkills,
|
||||
getSkillByName: db.getSkillByName,
|
||||
listSkillsByAccess: skillDbMethods.listSkillsByAccess,
|
||||
listAlwaysApplySkills: skillDbMethods.listAlwaysApplySkills,
|
||||
getSkillByName: skillDbMethods.getSkillByName,
|
||||
},
|
||||
);
|
||||
agentConfigs.set(agentId, config);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ const {
|
|||
getStorageMetadata,
|
||||
resolveRequestTenantId,
|
||||
enrichWithSkillConfigurable,
|
||||
mergeDeploymentSkillIds,
|
||||
createDeploymentSkillMethods,
|
||||
isDeploymentSkillFileSource,
|
||||
getDeploymentSkillDownloadStream,
|
||||
} = require('@librechat/api');
|
||||
const {
|
||||
Permissions,
|
||||
|
|
@ -27,6 +31,34 @@ const { checkPermission, grantPermission } = require('~/server/services/Permissi
|
|||
const { getFileStrategy } = require('~/server/utils/getFileStrategy');
|
||||
const db = require('~/models');
|
||||
|
||||
const deploymentSkillMethods = createDeploymentSkillMethods({
|
||||
getSkillById: db.getSkillById,
|
||||
getSkillByName: db.getSkillByName,
|
||||
listSkillsByAccess: db.listSkillsByAccess,
|
||||
listAlwaysApplySkills: db.listAlwaysApplySkills,
|
||||
listSkillFiles: db.listSkillFiles,
|
||||
getSkillFileByPath: db.getSkillFileByPath,
|
||||
updateSkillFileContent: db.updateSkillFileContent,
|
||||
updateSkillFileCodeEnvIds: db.updateSkillFileCodeEnvIds,
|
||||
});
|
||||
|
||||
function getSkillDbMethods() {
|
||||
return deploymentSkillMethods;
|
||||
}
|
||||
|
||||
function withDeploymentSkillIds(ids = []) {
|
||||
return mergeDeploymentSkillIds(ids);
|
||||
}
|
||||
|
||||
function getSkillStrategyFunctions(source) {
|
||||
if (isDeploymentSkillFileSource(source)) {
|
||||
return {
|
||||
getDownloadStream: (_req, filepath) => getDeploymentSkillDownloadStream(filepath),
|
||||
};
|
||||
}
|
||||
return getStrategyFunctions(source);
|
||||
}
|
||||
|
||||
function resolveSkillStorage(req, { isImage = false } = {}) {
|
||||
const source = getFileStrategy(req.config, { context: FileContext.skill_file, isImage });
|
||||
const strategy = getStrategyFunctions(source);
|
||||
|
|
@ -290,7 +322,7 @@ function enrichLoadedToolsWithAgentContext({ result, req, ctx = {}, fallback = {
|
|||
|
||||
/** Skill-related properties for ToolExecuteOptions (stable references, allocated once). */
|
||||
const skillToolDeps = {
|
||||
getSkillByName: db.getSkillByName,
|
||||
getSkillByName: deploymentSkillMethods.getSkillByName,
|
||||
getAuthorSkillByName,
|
||||
createSkill: db.createSkill,
|
||||
updateSkill: db.updateSkill,
|
||||
|
|
@ -299,14 +331,14 @@ const skillToolDeps = {
|
|||
canEditSkill,
|
||||
grantSkillOwner,
|
||||
saveSkillFileContent,
|
||||
listSkillFiles: db.listSkillFiles,
|
||||
getStrategyFunctions,
|
||||
listSkillFiles: deploymentSkillMethods.listSkillFiles,
|
||||
getStrategyFunctions: getSkillStrategyFunctions,
|
||||
batchUploadCodeEnvFiles,
|
||||
getSessionInfo,
|
||||
checkIfActive,
|
||||
updateSkillFileCodeEnvIds: db.updateSkillFileCodeEnvIds,
|
||||
getSkillFileByPath: db.getSkillFileByPath,
|
||||
updateSkillFileContent: db.updateSkillFileContent,
|
||||
updateSkillFileCodeEnvIds: deploymentSkillMethods.updateSkillFileCodeEnvIds,
|
||||
getSkillFileByPath: deploymentSkillMethods.getSkillFileByPath,
|
||||
updateSkillFileContent: deploymentSkillMethods.updateSkillFileContent,
|
||||
/**
|
||||
* `read_file` falls back to a sandbox `cat` for `/mnt/data/...` paths
|
||||
* and for `{firstSegment}/...` paths whose first segment isn't a known
|
||||
|
|
@ -327,6 +359,9 @@ module.exports = {
|
|||
getSkillToolDeps,
|
||||
canAuthorSkillFiles,
|
||||
isAgentSkillsEnabledForRun,
|
||||
getSkillDbMethods,
|
||||
withDeploymentSkillIds,
|
||||
getSkillStrategyFunctions,
|
||||
enrichWithSkillConfigurable,
|
||||
buildSkillPrimedIdsByName,
|
||||
buildAgentToolContext,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ const mockGetStrategyFunctions = jest.fn();
|
|||
const mockGetFileStrategy = jest.fn();
|
||||
const mockGetStorageMetadata = jest.fn();
|
||||
const mockResolveRequestTenantId = jest.fn();
|
||||
const mockCreateDeploymentSkillMethods = jest.fn((methods) => methods);
|
||||
|
||||
jest.mock('~/server/services/Files/strategies', () => ({
|
||||
getStrategyFunctions: (...args) => mockGetStrategyFunctions(...args),
|
||||
|
|
@ -22,8 +23,12 @@ jest.mock('~/server/services/Files/Code/process', () => ({
|
|||
|
||||
jest.mock('@librechat/api', () => ({
|
||||
checkAccess: jest.fn(),
|
||||
createDeploymentSkillMethods: (...args) => mockCreateDeploymentSkillMethods(...args),
|
||||
enrichWithSkillConfigurable: jest.fn(),
|
||||
getDeploymentSkillDownloadStream: jest.fn(),
|
||||
getStorageMetadata: (...args) => mockGetStorageMetadata(...args),
|
||||
isDeploymentSkillFileSource: jest.fn(() => false),
|
||||
mergeDeploymentSkillIds: jest.fn((ids = []) => ids),
|
||||
resolveRequestTenantId: (...args) => mockResolveRequestTenantId(...args),
|
||||
}));
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue