Two compounding bugs from the post-#87 extraction caught by today's template sweep (matches the same fixes already shipped in claude-code and gemini-cli): 1. adapter.py never aliased LangGraphAdapter to the contract name `Adapter`, which `molecule_runtime.adapters.get_adapter()` reads via `getattr(mod, "Adapter")`. Without it, every workspace startup fails preflight with "no \`Adapter\` class is exported". 2. Two bare imports of runtime modules (`from agent`, `from a2a_executor`) never got qualified to `from molecule_runtime.X import Y`. They worked when runtime was bundled into workspace/ but explode in the standalone template repo with ModuleNotFoundError. Same migration debt class fixed for hermes (a2a-sdk migration), claude-code (5 imports), gemini-cli (4 imports + alias) earlier today. Both gates now in molecule-ci PR #8 will catch this class of bug at PR time going forward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
54 lines
1.8 KiB
Python
54 lines
1.8 KiB
Python
"""LangGraph adapter — Python-based ReAct agent with skills, tools, and plugins."""
|
|
|
|
import os
|
|
import logging
|
|
|
|
from molecule_runtime.adapters.base import BaseAdapter, AdapterConfig
|
|
from a2a.server.agent_execution import AgentExecutor
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class LangGraphAdapter(BaseAdapter):
|
|
|
|
@staticmethod
|
|
def name() -> str:
|
|
return "langgraph"
|
|
|
|
@staticmethod
|
|
def display_name() -> str:
|
|
return "LangGraph"
|
|
|
|
@staticmethod
|
|
def description() -> str:
|
|
return "LangGraph ReAct agent — Python-based with skills, tools, plugins, and peer coordination"
|
|
|
|
@staticmethod
|
|
def get_config_schema() -> dict:
|
|
return {
|
|
"model": {"type": "string", "description": "LangChain model string (e.g. openrouter:google/gemini-2.5-flash)"},
|
|
"skills": {"type": "array", "items": {"type": "string"}, "description": "Skill folder names to load"},
|
|
"tools": {"type": "array", "items": {"type": "string"}, "description": "Built-in tools (web_search, filesystem, etc.)"},
|
|
}
|
|
|
|
def __init__(self):
|
|
self.loaded_skills = []
|
|
self.all_tools = []
|
|
self.system_prompt = None
|
|
|
|
async def setup(self, config: AdapterConfig) -> None:
|
|
result = await self._common_setup(config)
|
|
self.loaded_skills = result.loaded_skills
|
|
self.all_tools = result.langchain_tools
|
|
self.system_prompt = result.system_prompt
|
|
|
|
async def create_executor(self, config: AdapterConfig) -> AgentExecutor:
|
|
from molecule_runtime.agent import create_agent
|
|
from molecule_runtime.a2a_executor import LangGraphA2AExecutor
|
|
|
|
agent = create_agent(config.model, self.all_tools, self.system_prompt)
|
|
return LangGraphA2AExecutor(agent, heartbeat=config.heartbeat, model=config.model)
|
|
|
|
|
|
Adapter = LangGraphAdapter
|