Migrating from LangChain
Incremental adoption — nothing to rewrite on day one
visvoai-core sits directly on LangGraph/LangChain primitives rather than
replacing them, so adopting it from an existing LangChain/LangGraph
codebase is incremental by construction.
What passes through unchanged
- Your
@toolfunctions andStructuredTools.as_toolrecognizes anyisinstance(obj, BaseTool)and returns it untouched — see Defining tools. Drop your existing tool list straight intocore_tools=[...]. - Any
BaseChatModelyou already construct.build_graph'smodelargument is typed toBaseChatModel— aChatOpenAI(),ChatAnthropic(), or anything else you built by hand works exactly as it did in a raw LangGraph app.visvoai-aiis optional, not a requirement. - The compiled graph itself.
build_graphreturns a standard LangGraphCompiledStateGraph—.invoke,.ainvoke,.stream,.astream_events(version="v2")all behave exactly as LangGraph documents. Any code you've written againstastream_eventsoutput shapes keeps working. - Any checkpointer.
checkpointer=accepts anyBaseCheckpointSaver—MemorySaver, a Postgres/SQLite checkpointer, whatever you already use.
What you get by switching the loop
Migrate the loop first, keep your tools as-is:
# before: your own StateGraph wiring an agent node + a ToolNode
# after:
from visvoai.core.runtime import AgentRuntime
graph = AgentRuntime().build_graph(
model=your_existing_model,
core_tools=your_existing_tools, # unchanged
system_prompt=your_existing_prompt,
checkpointer=your_existing_checkpointer, # unchanged
)This alone buys you the soft step cap
(no more GraphRecursionError crashing a user-facing turn) without
touching a single tool definition.
Then, at your own pace: simplify tools
Once the loop is migrated, tools written as verbose StructuredTool
boilerplate can be simplified to plain functions — type hints become the
schema, the docstring becomes the description, no LangChain imports in the
file at all (see Defining tools). Or move a
tool that wants config/persistence to BaseAgentTool if it needs the
lifecycle hooks. Nothing forces this; both StructuredTool and plain
functions coexist forever in the same core_tools list.
What doesn't carry over
Anything from the LangChain "batteries" ecosystem that isn't a
BaseChatModel or a BaseTool — document loaders, retrievers, chains,
memory abstractions beyond a BaseCheckpointSaver — is out of scope for
visvoai-core entirely; it's a loop, not a framework. Keep using LangChain
directly for those and hand the results to your tools as plain Python. See
When not to use this if that describes most
of what you need.
Next: When not to use this.