Migrating from 0.x to 1.0¶
This guide covers everything that changes between the final 0.24.x releases and selectools v1.0.0. It is short on purpose: 1.0 is a stability declaration, not a rewrite. If your code runs warning-free on 0.24.x with python -W error::DeprecationWarning, you are already 1.0-ready except for the items below.
For competitor migrations (LangChain, CrewAI, etc.) see Migration Guides.
What semver means at 1.0¶
From v1.0.0 onward, selectools follows semantic versioning over its stable surface, as defined in the Deprecation Policy:
- Every public symbol carries a stability marker:
@stable,@beta, or@deprecated. @stableAPIs are frozen. Breaking changes to any@stablesymbol require v2.0.0.@betaAPIs are excluded from the semver guarantee. They may change in a minor release (1.1, 1.2, ...) without a deprecation cycle. The marker — not the version number — is the contract.- Deprecations continue to follow the two-minor-release window: anything deprecated in 1.X is not removed before 1.X+2.
Check a symbol's marker at runtime via obj.__stability__, or see Stability Markers.
Breaking change: AgentConfig.hooks removed¶
The dict-based hooks parameter — deprecated since the observer system shipped (see ADR-002) — is removed in 1.0. AgentConfig(hooks={...}) is no longer accepted. Migrate to AgentObserver (or AsyncAgentObserver for async handlers), which receives richer data: a stable run_id for the whole run, a call_id per tool invocation, the resolved system prompt, and structured results.
Before (0.x):
from selectools import Agent, AgentConfig
def log_tool(tool_name, tool_args):
print(f"-> {tool_name}({tool_args})")
agent = Agent(
[search_tool],
config=AgentConfig(
hooks={
"on_tool_start": log_tool,
"on_agent_end": lambda response, usage: print(usage),
}
),
)
After (1.0):
from selectools import Agent, AgentConfig, AgentObserver
class LoggingObserver(AgentObserver):
def on_tool_start(self, run_id, call_id, tool_name, tool_args):
print(f"-> {tool_name}({tool_args})")
def on_run_end(self, run_id, result):
print(result.usage)
agent = Agent([search_tool], config=AgentConfig(observers=[LoggingObserver()]))
Hook → observer method mapping¶
| 0.x hook key | 1.0 observer method | Notes |
|---|---|---|
on_agent_start | on_run_start(run_id, messages, system_prompt) | gains run_id + resolved system prompt |
on_agent_end | on_run_end(run_id, result) | result is the full AgentResult (use result.usage) |
on_iteration_start | on_iteration_start(run_id, iteration, messages) | gains run_id; iteration is 1-based |
on_iteration_end | on_iteration_end(run_id, iteration, response) | gains run_id |
on_tool_start | on_tool_start(run_id, call_id, tool_name, tool_args) | call_id matches the paired end/error event, even with parallel tools |
on_tool_chunk | on_tool_chunk(run_id, call_id, tool_name, chunk) | |
on_tool_end | on_tool_end(run_id, call_id, tool_name, result, duration_ms) | duration is now milliseconds |
on_tool_error | on_tool_error(run_id, call_id, tool_name, error, tool_args, duration_ms) | |
on_llm_start | on_llm_start(run_id, messages, model, system_prompt) | |
on_llm_end | on_llm_end(run_id, response, usage) | |
on_error | on_error(run_id, error, context) |
Override only the methods you need — all observer methods are optional no-ops on the base class. Ready-made observers: LoggingObserver, SimpleStepObserver, plus OTelObserver and LangfuseObserver in the [observe] extra.
Rename: Agent._clone_for_isolation() → Agent.clone_for_isolation()¶
The agent-cloning method used for concurrency isolation (eval suites, A2A task handling, planning) is promoted to public API at 1.0 and loses its underscore prefix. If you called the private name directly:
Behavior is unchanged: the clone shares tools and provider configuration but gets isolated memory and fresh usage accounting.
__all__ additions (not breaking)¶
Symbols that were importable but missing from selectools.__all__ are now declared. This matters if you use from selectools import * or lint against the public API:
- Pipeline family:
Pipeline,Step,StepResult,step,branch,parallel,retry,cache_step,compose - Router provider:
RouterProvider,RouterConfig(these two were previously importable only fromselectools.providers; the top-level re-export is new)
Existing from selectools import Pipeline-style imports keep working as before; they are simply official now. from selectools import RouterProvider becomes available for the first time.
Python 3.9 support dropped¶
v1.0.0 raises the floor to Python 3.10 (requires-python = ">=3.10"), planned to land with the 1.0 tag itself. Python 3.9 reached upstream end-of-life in October 2025.
- The last series supporting 3.9 is 0.24.x, which moves to security-fixes-only when 1.0 ships (see SECURITY.md).
- If you are stuck on 3.9, pin
selectools<1.0.
No other breaking changes¶
Pending final confirmation
The statement below is finalized by the pre-1.0 stability-marking sweep. Until the 1.0.0 tag is cut, treat this section as provisional.
Aside from the AgentConfig.hooks removal, the clone_for_isolation rename, and the Python 3.9 drop, no other breaking changes are planned for 1.0. Code that runs deprecation-warning-free on the latest 0.24.x release should run unmodified on 1.0 (on Python 3.10+).
Symbols promoted to stable at 1.0¶
The pre-1.0 marking sweep promotes proven @beta APIs (2+ releases, no breaking changes) to @stable:
| Symbol | Module | 0.x marker | 1.0 marker |
|---|---|---|---|
AgentGraph | selectools.orchestration | @beta | @stable |
GraphState | selectools.orchestration | @beta | @stable |
MergePolicy | selectools.orchestration | @beta | @stable |
ContextMode | selectools.orchestration | @beta | @stable |
InterruptRequest | selectools.orchestration | @beta | @stable |
Scatter | selectools.orchestration | @beta | @stable |
CheckpointStore | selectools.orchestration | @beta | stable (registry) |
InMemoryCheckpointStore | selectools.orchestration | @beta | @stable |
FileCheckpointStore | selectools.orchestration | @beta | @stable |
SQLiteCheckpointStore | selectools.orchestration | @beta | @stable |
SupervisorAgent | selectools.orchestration | @beta | @stable |
PlanAndExecuteAgent | selectools.patterns | @beta | @stable |
ReflectiveAgent | selectools.patterns | @beta | @stable |
DebateAgent | selectools.patterns | @beta | @stable |
TeamLeadAgent | selectools.patterns | @beta | @stable |
trace_to_html | selectools.trace | @beta | @stable |
SimpleStepObserver | selectools.observer | @beta | @stable |
RedisSessionStore | selectools.sessions | @beta | @stable |
AzureOpenAIProvider | selectools.providers | @beta | @stable |
Companion types of the promoted APIs (result/config/event dataclasses, enums, and graph helper functions such as GraphResult, GraphEvent, GraphNode, PlanStep, ReflectiveResult, DebateResult, TeamLeadResult, SupervisorStrategy, CheckpointMetadata, goto, update, merge_states, trace_to_json) were previously unmarked and are now @stable alongside their parent APIs.
CheckpointStore is @runtime_checkable and therefore carries its marker in selectools.stability.STABILITY_REGISTRY instead of a class attribute (a class attribute would become a structural protocol member on Python 3.9-3.11 and break isinstance() for third-party stores).
Deliberately not promoted (still @beta at 1.0): the loop-detection family, LocalProvider (test stub), the Pipeline/compose family, everything shipped in v0.24/v0.25-dev (pending, results, a2a, AgentAPI, knowledge backends, unified memory, LiteLLMProvider/RouterProvider, sanitizers, planning, compression/HITL types), and the rag/mcp/embeddings/observe/evals (evaluators) surfaces.
Anything still @beta at 1.0 (for example, the selectools serve CLI surface) remains outside the semver guarantee until promoted in a later 1.x release.