UNPKG

agent-contracts-runtime

Version:

Runtime bridge for executing agent-contracts workflows on Agent SDKs

185 lines (184 loc) 6.35 kB
import { getBlockingViolations } from "../chunk-MGRZBGQL.js"; // src/adapters/adk-sdk.ts function sanitizeAgentName(name) { let id = name.replace(/[^a-zA-Z0-9_]/g, "_"); if (id.length === 0 || /^[0-9]/.test(id)) id = `agent_${id}`; if (id === "user") id = "user_agent"; return id; } function extractEventText(event) { const parts = event.content?.parts; if (!parts) return ""; return parts.map((p) => p.text ?? "").join(""); } var AdkSdkAdapter = class _AdkSdkAdapter { apiKey; model; rootAgentName; guardrailHooks; cacheConfig; lastMemoryRef = null; runCounter = 0; sdk = null; constructor(config = {}) { this.apiKey = config.apiKey; this.model = config.model ?? "gemini-2.5-flash"; this.rootAgentName = config.rootAgentName ?? "root_agent"; this.guardrailHooks = config.guardrailHooks; this.cacheConfig = config.cacheConfig ?? { enabled: true }; } // ------------------------------------------------------------------------- // Internal helpers // ------------------------------------------------------------------------- async resolveSdk() { if (this.sdk) return this.sdk; const mod = await import("@google/adk"); this.sdk = { LlmAgent: mod.LlmAgent, InMemoryRunner: mod.InMemoryRunner, isFinalResponse: mod.isFinalResponse, stringifyContent: mod.stringifyContent, Gemini: mod.Gemini }; return this.sdk; } /** Resolve the `model` field for LlmAgent — a Gemini instance when an explicit * API key is supplied, otherwise the bare model id (ADK reads env keys). */ resolveModel(sdk) { if (this.apiKey) { return new sdk.Gemini({ model: this.model, apiKey: this.apiKey }); } return this.model; } /** Build the root LlmAgent with candidate sub-agents registered for transfer. */ buildRootAgent(sdk, instruction, agents) { const subAgents = (agents ?? []).map((a) => { const config = { name: sanitizeAgentName(a.name), description: a.description, instruction: a.prompt, model: a.model ?? this.resolveModel(sdk) }; if (a.tools) config.tools = a.tools; return new sdk.LlmAgent(config); }); const rootConfig = { name: sanitizeAgentName(this.rootAgentName), instruction, model: this.resolveModel(sdk) }; if (subAgents.length > 0) rootConfig.subAgents = subAgents; return new sdk.LlmAgent(rootConfig); } async runAgent(instruction, userMessage, agents, onProgress) { if (this.guardrailHooks) { const results = this.guardrailHooks.runChecks({ command: userMessage }); const blocking = getBlockingViolations(results); if (blocking.length > 0) { const msg = blocking.map((r) => `[${r.guardrail_id}] ${r.message}`).join("\n"); throw new Error(msg); } } const sdk = await this.resolveSdk(); const root = this.buildRootAgent(sdk, instruction, agents); const runner = new sdk.InMemoryRunner({ agent: root }); let finalText = ""; const stream = runner.runEphemeral({ userId: "agent-runtime", newMessage: { role: "user", parts: [{ text: userMessage }] } }); for await (const event of stream) { if (onProgress) emitProgressFromAdkEvent(event, onProgress); const ev = event; if (ev.errorCode || ev.errorMessage) { throw new Error( `Gemini API error (${ev.errorCode ?? "unknown"}): ${ev.errorMessage ?? "no details"}` ); } if (sdk.isFinalResponse(event) && event.content) { const text = sdk.stringifyContent(event) || extractEventText(event); if (text) finalText = text; } } return finalText; } nextMemoryRef() { return { id: `adk-run-${++this.runCounter}-${Date.now()}`, provider: "google-adk", compat: `google-adk@${this.model}`, created_at: (/* @__PURE__ */ new Date()).toISOString() }; } // ------------------------------------------------------------------------- // SdkAdapter interface // ------------------------------------------------------------------------- async send(prompt, options) { this.lastMemoryRef = null; const split = options.splitPrompt; let instruction = ""; let userMessage; if (split && this.cacheConfig.enabled !== false) { instruction = split.system.join("\n\n---\n\n"); userMessage = split.user; } else { userMessage = prompt; } const text = await this.runAgent(instruction, userMessage, options.agents, options.onProgress); this.lastMemoryRef = this.nextMemoryRef(); return text; } async sendExecution(request) { this.lastMemoryRef = null; const split = request.splitPrompt; let instruction = ""; let userMessage; if (split && this.cacheConfig.enabled !== false) { instruction = split.system.join("\n\n---\n\n"); userMessage = split.user; } else { userMessage = request.prompt; } const agents = request.options.agents ?? request.agents; const text = await this.runAgent(instruction, userMessage, agents, request.options.onProgress); this.lastMemoryRef = this.nextMemoryRef(); return text; } getLastMemoryRef() { return this.lastMemoryRef; } isCompatible(compat) { return compat.startsWith("google-adk@"); } // ------------------------------------------------------------------------- // Test support // ------------------------------------------------------------------------- /** * Inject the resolved ADK SDK surface for testing. * Bypasses the dynamic import of @google/adk and the API key requirement. */ static withSdk(sdk, config) { const adapter = new _AdkSdkAdapter(config); adapter.sdk = sdk; return adapter; } }; function emitProgressFromAdkEvent(event, onProgress) { const parts = event.content?.parts; if (!parts) return; for (const part of parts) { if (part.functionCall?.name) { onProgress({ type: "tool_use", tool_name: part.functionCall.name, input: part.functionCall.args }); } else if (part.functionResponse?.name) { onProgress({ type: "tool_result", tool_name: part.functionResponse.name }); } else if (part.text) { onProgress({ type: "text", message: part.text }); } } } export { AdkSdkAdapter }; //# sourceMappingURL=adk-sdk.js.map