@xynehq/jaf
Version:
Juspay Agent Framework - A purely functional agent framework with immutable state and composable tools
250 lines • 8.95 kB
JavaScript
/**
* Pure functional Agent Card generation
* Transforms JAF agents into A2A Agent Cards
*/
// Pure function to generate Agent Card from A2A agents
export const generateAgentCard = (config, agents, baseUrl = 'http://localhost:3000') => ({
protocolVersion: '0.3.0',
name: config.name,
description: config.description,
url: `${baseUrl}/a2a`,
preferredTransport: 'JSONRPC',
version: config.version,
provider: config.provider,
capabilities: {
streaming: true,
pushNotifications: false,
stateTransitionHistory: true
},
defaultInputModes: ['text/plain', 'application/json'],
defaultOutputModes: ['text/plain', 'application/json'],
skills: generateSkillsFromAgents(agents),
securitySchemes: generateSecuritySchemes(),
security: generateSecurityRequirements()
});
// Pure function to generate skills from A2A agents
export const generateSkillsFromAgents = (agents) => {
const skills = [];
agents.forEach((agent, agentName) => {
// Create a main skill for the agent
const mainSkill = {
id: `${agentName}-main`,
name: agent.name,
description: agent.description,
tags: ['general', ...agent.tools.map(tool => tool.name)],
examples: generateExamplesForAgent(agent),
inputModes: agent.supportedContentTypes,
outputModes: agent.supportedContentTypes
};
skills.push(mainSkill);
// Create individual skills for each tool
agent.tools.forEach(tool => {
const toolSkill = {
id: `${agentName}-${tool.name}`,
name: tool.name,
description: tool.description,
tags: [tool.name, 'tool', agentName],
examples: generateExamplesForTool(tool),
inputModes: ['text/plain', 'application/json'],
outputModes: ['text/plain', 'application/json']
};
skills.push(toolSkill);
});
});
return skills;
};
// Pure function to generate examples for an agent
export const generateExamplesForAgent = (agent) => {
const baseExamples = [
`Ask ${agent.name} for help`,
`What can ${agent.name} do?`
];
// Add tool-specific examples
const toolExamples = agent.tools.slice(0, 2).map(tool => `Use ${tool.name} to ${tool.description.toLowerCase()}`);
return [...baseExamples, ...toolExamples];
};
// Pure function to generate examples for a tool
export const generateExamplesForTool = (tool) => [
`Use ${tool.name}`,
tool.description,
`Help me with ${tool.name.replace(/_/g, ' ')}`
];
// Pure function to generate security schemes
export const generateSecuritySchemes = () => ({
bearerAuth: {
type: 'http',
scheme: 'bearer',
description: 'Bearer token authentication'
},
apiKey: {
type: 'apiKey',
in: 'header',
name: 'X-API-Key',
description: 'API key authentication'
}
});
// Pure function to generate security requirements
export const generateSecurityRequirements = () => [
// No authentication required by default
{},
// Optional bearer auth
{ bearerAuth: [] },
// Optional API key
{ apiKey: [] }
];
// Pure function to generate Agent Card for a specific agent
export const generateAgentCardForAgent = (agentName, agent, config = {}, baseUrl = 'http://localhost:3000') => {
const agentMap = new Map([[agentName, agent]]);
const cardConfig = {
name: config.name || agent.name,
description: config.description || agent.description,
version: config.version || '1.0.0',
provider: config.provider || {
organization: 'JAF Agent',
url: 'https://functional-agent-framework.com'
}
};
return generateAgentCard(cardConfig, agentMap, baseUrl);
};
// Pure function to validate Agent Card
export const validateAgentCard = (card) => {
const errors = [];
// Required fields validation
if (!card.name?.trim()) {
errors.push('Agent card name is required');
}
if (!card.description?.trim()) {
errors.push('Agent card description is required');
}
if (!card.url?.trim()) {
errors.push('Agent card URL is required');
}
if (!card.version?.trim()) {
errors.push('Agent card version is required');
}
if (!card.protocolVersion?.trim()) {
errors.push('Protocol version is required');
}
// Skills validation
if (!card.skills || card.skills.length === 0) {
errors.push('At least one skill is required');
}
else {
card.skills.forEach((skill, index) => {
if (!skill.id?.trim()) {
errors.push(`Skill ${index}: ID is required`);
}
if (!skill.name?.trim()) {
errors.push(`Skill ${index}: Name is required`);
}
if (!skill.description?.trim()) {
errors.push(`Skill ${index}: Description is required`);
}
if (!skill.tags || skill.tags.length === 0) {
errors.push(`Skill ${index}: At least one tag is required`);
}
});
}
// Input/Output modes validation
if (!card.defaultInputModes || card.defaultInputModes.length === 0) {
errors.push('At least one default input mode is required');
}
if (!card.defaultOutputModes || card.defaultOutputModes.length === 0) {
errors.push('At least one default output mode is required');
}
// URL validation
if (card.url && !isValidUrl(card.url)) {
errors.push('Invalid URL format');
}
return {
isValid: errors.length === 0,
errors
};
};
// Pure helper function to validate URL
const isValidUrl = (url) => {
try {
new URL(url);
return true;
}
catch {
return false;
}
};
// Pure function to create minimal Agent Card
export const createMinimalAgentCard = (name, description, url = 'http://localhost:3000/a2a') => ({
protocolVersion: '0.3.0',
name,
description,
url,
preferredTransport: 'JSONRPC',
version: '1.0.0',
capabilities: {
streaming: true,
pushNotifications: false,
stateTransitionHistory: false
},
defaultInputModes: ['text/plain'],
defaultOutputModes: ['text/plain'],
skills: [{
id: 'general',
name: 'General Assistant',
description: 'General purpose assistance',
tags: ['general', 'assistant'],
examples: ['How can I help you?']
}]
});
// Pure function to merge multiple Agent Cards
export const mergeAgentCards = (baseCard, ...additionalCards) => {
const mergedSkills = [
...baseCard.skills,
...additionalCards.flatMap(card => card.skills)
];
// Remove duplicate skills by ID
const uniqueSkills = mergedSkills.filter((skill, index, arr) => arr.findIndex(s => s.id === skill.id) === index);
const mergedInputModes = Array.from(new Set([
...baseCard.defaultInputModes,
...additionalCards.flatMap(card => card.defaultInputModes)
]));
const mergedOutputModes = Array.from(new Set([
...baseCard.defaultOutputModes,
...additionalCards.flatMap(card => card.defaultOutputModes)
]));
return {
...baseCard,
skills: uniqueSkills,
defaultInputModes: mergedInputModes,
defaultOutputModes: mergedOutputModes,
capabilities: {
streaming: baseCard.capabilities.streaming || additionalCards.some(card => card.capabilities.streaming),
pushNotifications: baseCard.capabilities.pushNotifications || additionalCards.some(card => card.capabilities.pushNotifications),
stateTransitionHistory: baseCard.capabilities.stateTransitionHistory || additionalCards.some(card => card.capabilities.stateTransitionHistory)
}
};
};
// Pure function to create Agent Card from configuration
export const createAgentCardFromConfig = (config) => {
const baseUrl = config.baseUrl || 'http://localhost:3000';
return {
protocolVersion: '0.3.0',
name: config.name,
description: config.description,
url: `${baseUrl}/a2a`,
preferredTransport: 'JSONRPC',
version: config.version || '1.0.0',
provider: config.provider || {
organization: 'JAF Framework',
url: 'https://functional-agent-framework.com'
},
capabilities: {
streaming: true,
pushNotifications: false,
stateTransitionHistory: true,
...config.capabilities
},
defaultInputModes: ['text/plain', 'application/json'],
defaultOutputModes: ['text/plain', 'application/json'],
skills: generateSkillsFromAgents(config.agents)
};
};
//# sourceMappingURL=agent-card.js.map