UNPKG

@xynehq/jaf

Version:

Juspay Agent Framework - A purely functional agent framework with immutable state and composable tools

436 lines 15.2 kB
/** * JAF ADK Layer - Type Converters * * Functional type conversion between ADK and Core formats */ import { z } from 'zod'; import { PartType, ContentRole, Model, ToolParameterType } from '../types.js'; // ========== Content Conversions ========== export const convertAdkContentToCoreMessage = (content) => { // Extract text content const textParts = content.parts.filter(p => String(p.type) === 'text'); const textContent = textParts.map(p => p.text || '').join(' ').trim(); // Extract function calls const functionCallParts = content.parts.filter(p => String(p.type) === 'function_call'); const toolCalls = functionCallParts .map(p => p.functionCall) .filter(fc => fc !== undefined) .map(fc => ({ id: fc.id, type: 'function', function: { name: fc.name, arguments: JSON.stringify(fc.args) } })); // Extract function responses const functionResponseParts = content.parts.filter(p => String(p.type) === 'function_response'); const functionResponse = functionResponseParts.length > 0 ? functionResponseParts[0].functionResponse : undefined; // Determine role - function responses should always be 'tool' role const role = functionResponse ? 'tool' : convertAdkRoleToCoreRole(content.role); const message = { role: role, content: textContent }; if (toolCalls.length > 0) { message.tool_calls = toolCalls; } if (functionResponse) { message.tool_call_id = functionResponse.id; message.content = typeof functionResponse.response === 'string' ? functionResponse.response : JSON.stringify(functionResponse.response); } return message; }; export const convertCoreMessageToAdkContent = (message) => { const parts = []; // Add text content if (message.content && message.content.trim()) { parts.push({ type: PartType.TEXT, text: message.content }); } // Add tool calls if (message.tool_calls) { for (const toolCall of message.tool_calls) { parts.push({ type: PartType.FUNCTION_CALL, functionCall: { id: toolCall.id, name: toolCall.function.name, args: safeJsonParse(toolCall.function.arguments) } }); } } // Add tool responses if (message.tool_call_id && message.role === 'tool') { parts.push({ type: PartType.FUNCTION_RESPONSE, functionResponse: { id: message.tool_call_id, name: 'unknown', // Core doesn't track function name in responses response: safeJsonParse(message.content || ''), success: true } }); } return { role: convertCoreRoleToAdkRole(message.role), parts, metadata: {} }; }; // ========== Role Conversions ========== export const convertAdkRoleToCoreRole = (adkRole) => { switch (adkRole) { case ContentRole.USER: case 'user': return 'user'; case ContentRole.MODEL: case 'model': return 'assistant'; case ContentRole.SYSTEM: case 'system': return 'system'; default: return 'user'; } }; export const convertCoreRoleToAdkRole = (coreRole) => { switch (coreRole) { case 'user': return ContentRole.USER; case 'assistant': return ContentRole.MODEL; case 'system': return ContentRole.SYSTEM; case 'tool': return ContentRole.MODEL; // Tool responses become model content default: return ContentRole.USER; } }; // ========== Session Conversions ========== export const convertAdkSessionToCoreState = (session, newMessage) => { const messages = session.messages.map(convertAdkContentToCoreMessage); if (newMessage) { messages.push(convertAdkContentToCoreMessage(newMessage)); } return { runId: session.id, // Type conversion needed traceId: session.id, // Type conversion needed messages, currentAgentName: 'default', // ADK doesn't have explicit agent names in sessions context: { userId: session.userId, sessionId: session.id, artifacts: session.artifacts, ...session.metadata.properties }, turnCount: session.messages.length }; }; export const convertCoreStateToAdkSession = (state) => { return { id: String(state.runId), appName: state.context?.appName || 'default', userId: state.context?.userId || 'unknown', messages: state.messages.map(convertCoreMessageToAdkContent), artifacts: state.context?.artifacts || {}, metadata: { created: new Date(), lastAccessed: new Date(), properties: { turnCount: state.turnCount, currentAgentName: state.currentAgentName } } }; }; // ========== Agent Conversions ========== export const convertAdkAgentToCoreAgent = (adkAgent) => { return { name: adkAgent.config.name, instructions: (state) => adkAgent.config.instruction, tools: adkAgent.config.tools?.map(convertAdkToolToCoreTool) || [], modelConfig: { name: convertAdkModelToCoreModel(adkAgent.config.model), temperature: 0.7, // Default, can be overridden maxTokens: 2000 // Default, can be overridden }, handoffs: [], outputCodec: adkAgent.config.outputSchema ? createZodFromAdkSchema(adkAgent.config.outputSchema) : undefined }; }; export const convertCoreAgentToAdkAgent = (coreAgent) => { return { id: coreAgent.name, config: { name: coreAgent.name, model: convertCoreModelToAdkModel(coreAgent.modelConfig?.name || 'gpt-4o'), instruction: typeof coreAgent.instructions === 'function' ? coreAgent.instructions({}) : String(coreAgent.instructions), description: `Agent: ${coreAgent.name}`, tools: coreAgent.tools?.map(convertCoreToolToAdkTool) || [], subAgents: [] }, metadata: { created: new Date(), version: '1.0.0' } }; }; // ========== Tool Conversions ========== export const convertAdkToolToCoreTool = (adkTool) => { return { schema: { name: adkTool.name, description: adkTool.description, parameters: createZodFromAdkParameters(adkTool.parameters) }, execute: async (params, context) => { // Convert Core context to ADK context const adkContext = convertCoreContextToAdkContext(context); const result = await adkTool.execute(params, adkContext); // Ensure result is string or ToolResult format if (typeof result === 'string') { return result; } // Convert ADK ToolResult to Core format if needed return result; } }; }; export const convertCoreToolToAdkTool = (coreTool) => { return { name: coreTool.schema.name, description: coreTool.schema.description, parameters: convertZodToAdkParameters(coreTool.schema.parameters), execute: async (params, context) => { // Convert ADK context to Core context const coreContext = convertAdkContextToCoreContext(context); return await coreTool.execute(params, coreContext); }, metadata: { source: 'function', version: '1.0.0' } }; }; // ========== Model Conversions ========== export const convertAdkModelToCoreModel = (adkModel) => { // Map specific model enum values to their Core equivalents switch (adkModel) { case Model.GEMINI_2_0_FLASH: case 'gemini-2.0-flash': return 'gemini-2.0-flash'; case Model.GEMINI_1_5_PRO: case 'gemini-1.5-pro': return 'gemini-1.5-pro'; case Model.GEMINI_1_5_FLASH: case 'gemini-1.5-flash': return 'gemini-1.5-flash'; case Model.GPT_4_TURBO: case 'gpt-4-turbo': return 'gpt-4-turbo'; case Model.GPT_4: case 'gpt-4': return 'gpt-4'; case Model.GPT_3_5_TURBO: case 'gpt-3.5-turbo': return 'gpt-3.5-turbo'; case Model.CLAUDE_3_OPUS_20240229: case 'claude-3-opus-20240229': return 'claude-3-opus'; case Model.CLAUDE_3_5_SONNET_LATEST: case 'claude-3-5-sonnet-latest': return 'claude-3-sonnet'; case Model.CLAUDE_3_HAIKU_20240307: case 'claude-3-haiku-20240307': return 'claude-3-haiku'; case Model.CUSTOM: case 'custom': return 'gpt-4o'; default: // For any other string model, return as-is if (typeof adkModel === 'string') { return adkModel; } return 'gpt-4o'; } }; export const convertCoreModelToAdkModel = (coreModel) => { switch (coreModel) { case 'gemini-2.0-flash': return Model.GEMINI_2_0_FLASH; case 'gemini-1.5-pro': return Model.GEMINI_1_5_PRO; case 'gemini-1.5-flash': return Model.GEMINI_1_5_FLASH; case 'gpt-4-turbo': return Model.GPT_4_TURBO; case 'gpt-4': return Model.GPT_4; case 'gpt-3.5-turbo': return Model.GPT_3_5_TURBO; case 'claude-3-opus': case 'claude-3-opus-20240229': return Model.CLAUDE_3_OPUS_20240229; case 'claude-3-sonnet': case 'claude-3-5-sonnet': case 'claude-3-5-sonnet-latest': return Model.CLAUDE_3_5_SONNET_LATEST; case 'claude-3-haiku': case 'claude-3-haiku-20240307': return Model.CLAUDE_3_HAIKU_20240307; default: return Model.CUSTOM; } }; // ========== Parameter Schema Conversions ========== export const createZodFromAdkParameters = (parameters) => { const shape = {}; for (const param of parameters) { let schema = createZodFromAdkParameter(param); if (!param.required) { schema = schema.optional(); } shape[param.name] = schema; } return z.object(shape); }; export const createZodFromAdkParameter = (param) => { switch (param.type) { case ToolParameterType.STRING: case 'string': if (param.enum) { return z.enum(param.enum); } return z.string().describe(param.description); case ToolParameterType.NUMBER: case 'number': return z.number().describe(param.description); case ToolParameterType.BOOLEAN: case 'boolean': return z.boolean().describe(param.description); case ToolParameterType.ARRAY: case 'array': if (param.items) { return z.array(createZodFromAdkParameter(param.items)).describe(param.description); } return z.array(z.any()).describe(param.description); case ToolParameterType.OBJECT: case 'object': if (param.properties) { const shape = {}; for (const [key, value] of Object.entries(param.properties)) { shape[key] = createZodFromAdkParameter(value); } return z.object(shape).describe(param.description); } return z.object({}).describe(param.description); default: return z.any().describe(param.description); } }; export const convertZodToAdkParameters = (zodSchema) => { // This is a simplified conversion - in practice, you'd need more sophisticated Zod introspection // For now, return a basic parameter structure return [{ name: 'input', type: ToolParameterType.OBJECT, description: 'Tool input parameters', required: true }]; }; export const createZodFromAdkSchema = (adkSchema) => { // Simple wrapper around ADK schema validation return z.any().refine((data) => { const result = adkSchema.validate(data); return result.success; }); }; // ========== Context Conversions ========== export const convertCoreContextToAdkContext = (coreContext) => { return { agent: coreContext.agent ? convertCoreAgentToAdkAgent(coreContext.agent) : undefined, session: coreContext.session ? convertCoreStateToAdkSession(coreContext.session) : undefined, message: coreContext.message ? convertCoreMessageToAdkContent(coreContext.message) : undefined, actions: coreContext.actions, metadata: coreContext.metadata }; }; export const convertAdkContextToCoreContext = (adkContext) => { return { agent: adkContext.agent ? convertAdkAgentToCoreAgent(adkContext.agent) : undefined, session: adkContext.session ? convertAdkSessionToCoreState(adkContext.session) : undefined, message: adkContext.message ? convertAdkContentToCoreMessage(adkContext.message) : undefined, actions: adkContext.actions, metadata: adkContext.metadata }; }; // ========== Utility Functions ========== export const safeJsonParse = (jsonString) => { try { return JSON.parse(jsonString); } catch { return jsonString; // Return as string if parsing fails } }; export const safeJsonStringify = (obj) => { try { return JSON.stringify(obj); } catch { return String(obj); } }; // ========== Function Call Utilities ========== export const extractFunctionCallsFromAdkContent = (content) => { return content.parts .filter(p => String(p.type) === 'function_call') .map(p => p.functionCall) .filter(fc => fc !== undefined); }; export const extractFunctionResponsesFromAdkContent = (content) => { return content.parts .filter(p => String(p.type) === 'function_response') .map(p => p.functionResponse) .filter(fr => fr !== undefined); }; export const createAdkContentWithText = (text, role = ContentRole.MODEL) => { return { role, parts: [{ type: PartType.TEXT, text }], metadata: {} }; }; export const createAdkContentWithFunctionCall = (functionCall, role = ContentRole.MODEL) => { return { role, parts: [{ type: PartType.FUNCTION_CALL, functionCall }], metadata: {} }; }; export const createAdkContentWithFunctionResponse = (functionResponse, role = ContentRole.MODEL) => { return { role, parts: [{ type: PartType.FUNCTION_RESPONSE, functionResponse }], metadata: {} }; }; //# sourceMappingURL=type-converters.js.map