All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
0.15.0 - 2026-01-25
Added
adapter_envoption: New configuration option forSnakeBridge.ConfigHelper.configure_snakepit!/1andsnakepit_config/1- Pass extra environment variables to the Python adapter (e.g.,
HF_HOME,TOKENIZERS_PARALLELISM,CUDA_VISIBLE_DEVICES) - Top-level
adapter_envis merged intopool_configfor single-pool setups - Per-pool
adapter_envoverrides top-level values in multi-pool configurations - Environment variables are merged alongside the computed
PYTHONPATH
- Pass extra environment variables to the Python adapter (e.g.,
- New module discovery modes for
generate: :all:module_mode: :exports/:api- derive submodules from root__all__exported module objects (no package walk)module_mode: :explicit- discover submodules but keep only modules/packages that define__all__in source- Added introspector flags
--exports-modeand--public-api-mode(used internally by module modes)
- Docs-driven public surface generation:
module_mode: :docs/:manifest- generate a docs-defined surface from a committed JSON manifest (no package walk)- New
python_depsoptions:docs_manifest:anddocs_profile: mix snakebridge.docs.manifest- build a manifest fromobjects.inv(+ optional HTML nav/summary pages for MkDocs/Sphinx sites)mix snakebridge.plan- preview estimated file counts for docs-manifest surfaces without running Python
- Class method guardrails:
- New
python_depsoptions:class_method_scope:andmax_class_methods: - Prevents runaway wrapper generation for inheritance-heavy classes by falling back to declared-only method enumeration
- New
- Used-mode scanning improvements:
- New
config :snakebridge, scan_extensions: [...]option to include.exs(scripts/examples) in used-symbol scanning
- New
- Missing symbol UX controls:
- New
python_depsoption:on_not_found:(:erroror:stub) - Prevents repeated compile-time retries for missing symbols
- New
- Docstring markdown sanitization:
- New
MarkdownSanitizermodule repairs common upstream docstring issues before ExDoc rendering - Closes unclosed fenced code blocks by detecting prose boundaries
- Fixes manpage-style backtick quotes (
sys.byteorder'→sys.byteorder)
- New
- Explicit regeneration task:
mix snakebridge.regenforces wrapper regeneration regardless of cache statemix snakebridge.regen --cleanremoves generated artifacts and metadata before regenerating
Fixed
- Markdown sanitizer no longer alters inline code containing apostrophes (e.g.,
dspy.LM(model='gpt-5', ...)); manpage quote fix now uses a stricter regex. generate: :allnow prunes stale manifest module-doc entries for the library, preventing old module wrapper files from sticking around after switching to a smaller module plan.- Docs manifest generation no longer treats class members (e.g.
pkg.Class.method) as importable modules. - Used-mode scanning no longer treats SnakeBridge wrapper helpers (e.g.
__functions__/0,__search__/1,doc/1, metadata) as Python symbols. - Used-mode supports
*_streamwrappers by resolving them against the base Python function and recording streaming support in the manifest. - Manifest loading now normalizes class module keys to canonical Elixir casing, fixing false “missing” calls for class modules (e.g.
Numpy.Ndarray). - Fixed intermittent
:enoentfailures in concurrent test/compile environments by making env/config reads process-overridable (no global env mutation needed for tests). - SnakeBridge runs now wait for Snakepit workers to complete the gRPC readiness handshake before executing calls, reducing intermittent stream errors on startup.
Internal
- Lock generator hash now includes the Python introspection script and module selection logic so behavior changes correctly trigger regeneration.
- Library config hashing now incorporates
docs_manifestcontent to ensure surface changes trigger regeneration. - Added SnakeBridge.Env module for process-scoped environment/config overrides (primarily for async test isolation).
- Upgraded snakepit to 0.12.0.
0.14.0 - 2026-01-23
Added
Task Supervision
- SnakeBridge.TaskSupervisor: New supervisor for async operations in the application tree
- Stream workers now run under TaskSupervisor instead of unsupervised
Task.start - Session cleanup tasks are supervised for reliable resource release
- Callback invocations execute in supervised tasks with
GenServer.reply/2
Callback Registry Deadlock Prevention
- Refactored
invoke/2to spawn async tasks instead of blockinghandle_call - Callbacks can now safely invoke other callbacks without deadlocking
- Registry remains responsive during long-running callback execution
- Added catch clause for non-exception throws in callback execution
ETS Table Ownership
- CacheOwner GenServer: New process to own long-lived ETS tables
- Tables
:snakebridge_docsand:snakebridge_exception_cachesurvive caller exit - Modules gracefully handle missing tables during compile-time tooling
- Removed on-demand table creation from Docs and DynamicException modules
Stream Timeout Support
- Added
:stream_timeoutruntime option forstream_dynamicoperations - Configurable timeout with
:infinitysupport for unlimited waits - Workers are killed on timeout to prevent resource leaks
- Default timeout available via
SnakeBridge.Defaults.runtime_default_stream_timeout/0
Session Cleanup Configuration
- Added
:session_cleanup_timeout_msapplication config option - Cleanup tasks pass timeout to
Runtime.release_session/2 - Default cleanup timeout of 10 seconds
Telemetry Robustness
- Added
telemetry_ready?/0checks before emitting events - Prevents crashes when telemetry tables don't exist during compilation
- Applied consistently across Runtime, Streamer, Telemetry, and IntrospectionRunner
Module Discovery System
- module_mode: New configuration option with three modes:
:root/:light- only the root module:public/:standard- discover submodules with public APIs:all/:nuclear- discover everything including private
- module_include/module_exclude: Fine-grained submodule control
- module_depth: Limit discovery depth (e.g., 1 = direct children only)
- Public mode uses static AST inspection to detect modules with public APIs
- Modules with
__all__or top-level definitions are included; private (_) excluded - Legacy
submodules/public_apioptions remain supported but deprecated
Lazy Import Handling
- Introspector now iterates
__all__to discover lazy-loaded classes - Handles
__getattr__patterns used by libraries with lazy public exports - Classes not visible to
inspect.getmembers()are now properly discovered
Method Collision Fix
- Python classes with both
__init__and a method namednewnow work correctly - The
newmethod is renamed topython_newto avoid arity collisions - Prevents "defaults multiple times" compilation errors in generated code
Build System Improvements
- Compilation progress output during introspection and generation phases
- Config hash tracking in lock file detects library option changes
- Auto-generates
priv/python/requirements.txtfrompython_depsin mix.exs - Modules that fail to import now record errors instead of being silently skipped
File Locking Improvements
- Replaced
:global.transwith file-based.lockmechanism - Retry logic with configurable attempts and backoff
- More reliable across distributed compilation scenarios
Documentation Generation
- Module files are now generated from module_docs even without symbols
- Error notes added to moduledoc when Python import fails during generation
- Split layout correctly handles submodule documentation
- Session cleanup failure telemetry: Emits
[:snakebridge, :session, :cleanup, :error]when best-effort cleanup fails
Changed
- Registry is now supervised at runtime while keeping lazy startup for compile-time tasks
Lock.VerifierusesLogger.warninginstead ofMix.shell().info- Extracted helper functions for cache table access patterns
Internal
- Added CacheOwnerTest verifying table ownership semantics
- Added callback tests for non-blocking invocation behavior
- Added nested callback invocation test proving deadlock prevention
- Added RuntimeStreamerTimeoutTest for timeout and worker cleanup
- Added RegistrySupervisionTest for idempotent start_link behavior
- Added SessionCleanupTelemetryTest with RuntimeClientStub for failure simulation
- Added fixture_lazy_import, fixture_module_modes, fixture_new_collision for testing
- New test suites: LazyImportIntrospectionTest, ModuleModeIntrospectionTest, NewMethodCollisionTest
- Introspector catches all import exceptions, not just ImportError
- JSON parser extracts objects from output with warning prefixes
- Pipeline clears stale classes when regenerating with new filters
- Reserved attribute names list prevents 'new' attribute collisions
- Updated ex_doc from 0.39.3 to 0.40.0
- Updated snakepit from v0.11.0 to v0.11.1
0.13.0 - 2026-01-21
Added
- Generated class wrappers now include
@moduledocfrom Python class docstrings - Generated constructors now include
@docfrom__init__method docstrings - Generated methods now include
@docfrom method docstrings - Documentation is automatically converted from Python formats (Google, NumPy, Sphinx, Epytext) to ExDoc Markdown
Added
- Sanitizes docstring Markdown links that escape the docs root (e.g.
../...) to avoid ExDoc missing-file warnings
0.12.0 - 2026-01-20
Added
- Split layout generation mode: New
generated_layout: :splitconfig option (now default) produces Python-shaped directory structure with__init__.exfiles for package modules.- Mirrors Python module paths:
examplelib.predict→examplelib/predict/__init__.ex - Class modules get separate files:
Examplelib.Predict.Widget→examplelib/predict/widget.ex - Registry tracks all generated files for split layout
- Mirrors Python module paths:
Generator.PathMappermodule: New path mapping utilities for Python-to-Elixir file path conversion.Generator.render_module_file/7: Renders individual module files for split layout.Generator.render_class_file/4: Renders standalone class module files.Class.render_class_standalone/3: Renders classes as top-level modules for split layout.
Changed
- Default layout is now
:split: Generated wrappers use Python-shaped directory structure by default. - Legacy single-file layout (
:single) remains available viaconfig :snakebridge, generated_layout: :single. - Registry entries now contain relative file paths from
generated_dirfor split layout.
0.11.0 - 2026-01-19
Fixed
- SnakeBridge compiler now short-circuits when bindings are already up to date (manifest/lock/generated files).
- Suppressed the compile-time "preparing bindings" banner and the extra compile hint by default.
- Examples now enable Snakepit's Python dependency auto-install to prevent missing gRPC/protobuf imports when running stdlib-only demos.
0.10.0 - 2026-01-19
Added
- Max coverage signature pipeline: Tiered signature sources (runtime, text_signature, runtime_hints, stubs, stubgen, variadic) with per-symbol
signature_sourcemetadata. - Doc source tiers: Runtime docs first, stub docs next, module doc fallback, with per-symbol
doc_sourcemetadata. - Stub resolution upgrades: Local
.pyi,types-<pkg>packages, optional typeshed, and overload metadata handling. - Stubgen fallback with caching for libraries without stubs.
- Strict signature thresholds (
strict_signatures,min_signature_tier) with per-library overrides. - Coverage reports (JSON + Markdown) with tier counts and structured issues.
- New examples:
coverage_report_example,stub_fallback_example, and updatedstrict_mode_example.
Changed
- Manifest schema now records signature/doc sources, missing reasons, and overload counts.
- Introspection issues are captured in coverage reports instead of emitting warnings during builds.
Fixed
- Class/module collisions are now disambiguated (with
Classsuffixes) and stale class entries are pruned to prevent duplicate module definitions. - Lowercase/invalid class module segments no longer produce invalid typespec aliases; segments are camelized and invalid aliases fall back to
term(). - Attribute accessors now avoid name collisions with methods by appending
_attrwhen needed. - Leading-underscore parameter names are normalized to avoid unused-variable warnings in generated wrappers.
- Prefer the Snakepit-managed venv (default:
priv/snakepit/python/venv) when resolving the Python runtime inConfigHelperto avoid environment mismatches.
Documentation
- README and Generated Wrappers guide now document max coverage configuration and signature tiers.
- Revamped README.md for clarity and conciseness with focused quick start.
- Added Configuration Reference and Coverage Reports guides.
- Improved HexDocs menu structure with organized module groups.
0.9.0 - 2026-01-11
Added
Graceful serialization for containers: When a Python result is a container (list, dict, tuple, set, frozenset) that contains non-serializable objects, SnakeBridge now preserves the container structure and ref-wraps ONLY the non-serializable leaf objects. This is a significant improvement over the previous behavior where any non-serializable item caused the entire container to become a single ref.
Example - history with a non-serializable
responsefield:{:ok, history} = SnakeBridge.call("examplelib_module", "get_history", []) # history is a list of maps - NOT a single opaque ref! for entry <- history do # Serializable fields are directly accessible IO.puts("Model: #{entry["model"]}, cost: #{entry["cost"]}") # Only the non-serializable field becomes a ref response = entry["response"] {:ok, id} = SnakeBridge.attr(response, "id") endCycle detection in serialization: Self-referential structures (e.g., a list containing itself) are now handled safely - cycles are detected and converted to refs to prevent infinite recursion.
Enhanced ref metadata: Ref payloads now include
type_nameand__type_name__fields containing the Python class name, enabling better inspection and debugging.Serialization helpers:
SnakeBridge.unserializable?/1andSnakeBridge.unserializable_info/1to detect and inspect non-JSON-serializable Python objects that were replaced with markers (Snakepit markers, not refs)- Delegates to
Snakepit.Serializationfor consistent behavior - Markers include type information; repr is opt-in via environment variables
- See Snakepit's graceful serialization guide for full documentation
- Delegates to
Comprehensive documentation guides: Added 10 in-depth guides covering all major SnakeBridge features including Getting Started, Universal FFI, Generated Wrappers, Refs and Sessions, Type System, Streaming, Error Handling, Telemetry, Best Practices, and Session Affinity. Available in
guides/and integrated into HexDocs with organized navigation.Python test suite for adapter: New comprehensive Python tests for
encode_resultbehavior intest_snakebridge_adapter.py, covering nested refs, cycle detection, and type metadata.Integration tests for graceful serialization: New Elixir tests verifying validation config structures, nested refs, and ref usability. Test helpers use real Python stdlib (
re.compile()patterns) instead of mocks.Graceful serialization demo in universal_ffi_example: Section 9 demonstrates mixing compiled regex patterns with serializable metadata, showing direct field access while patterns remain as usable refs.
Atom round-trip preservation: When
SNAKEBRIDGE_ATOM_CLASS=true, PythonAtomobjects returned from functions now encode as tagged atoms ({"__type__": "atom", ...}) rather than becoming refs, preserving the expected round-trip semantics.
Changed
Container serialization behavior (breaking): Non-serializable items inside containers no longer force the whole container into a ref. Instead, only the leaf values that cannot be serialized become refs. This is the new default and only behavior - no feature flag or opt-out.
Before:
# Python returns: [{"model": "gpt-4", "response": <ModelResponse>}] # Elixir received: %SnakeBridge.Ref{} # The entire list was wrapped!After:
# Python returns: [{"model": "gpt-4", "response": <ModelResponse>}] # Elixir receives: [%{"model" => "gpt-4", "response" => %SnakeBridge.Ref{}}]Explicit arity generation for optional positional parameters: Functions with optional positional parameters now generate multiple explicit arities instead of using the variadic args list pattern. This provides better compile-time checking and clearer generated code.
Class module names are properly camelized: Class modules like
numpy.ndarrayare now generated asNumpy.Ndarrayinstead ofNumpy.ndarray, following Elixir naming conventions.Attribute name sanitization: Class attributes with special characters (e.g.,
T,mT) are now sanitized to valid Elixir identifiers (t,m_t).OTP logger level alignment:
ConfigHelper.configure_snakepit!/1now aligns the OTP logger level with the Elixir logger level to suppress low-level SSL and public_key logs.Examples enable numpy by default: The
signature_showcaseandclass_resolution_exampleexamples now include numpy without requiring theSNAKEBRIDGE_EXAMPLE_NUMPY=1environment variable.
Fixed
- Suppress ssl/public_key CA loading logs instead of tls_certificate_check logs during application startup.
Internal
- Schema version alignment: Tagged values (bytes, datetime, tuple, set, dict, etc.)
now use
SCHEMA_VERSION; refs and stream_refs useREF_SCHEMA_VERSION. This prevents future breakage if the constants diverge. - Async generator exclusion centralized:
_is_generator_or_iteratorand_is_streamablenow explicitly exclude async generators (they cannot be consumed vianext()). - Broad exception fallback:
encode_resultcatches anyExceptionduring encoding and falls back to ref-wrapping, preventing hard failures from concurrent mutation, weird__iter__implementations, or deeply nested structures. - Memoization for refs/stream_refs:
ref_memodict tracks created refs to ensure the same object yields the same ref id (deduplication). - Container snapshotting: List, dict, set, and frozenset iteration now snapshots the container first to reduce "changed size during iteration" errors.
_is_json_saferecursion guard: The safety-net validation is wrapped intry/except RecursionErrorto prevent crashes on near-limit structures.- Fallback ref cleanup: On encoding failure, any refs created during partial encoding are removed from the registry to prevent unreachable ref leakage.
0.8.2 - 2026-01-11
Added
- Pool name propagation for refs: Refs now retain the originating
pool_namewhen provided via__runtime__: [pool_name: :my_pool]SnakeBridge.RefandSnakeBridge.StreamRefstructs include apool_namefield- Subsequent
get_attr,call_method,set_attr, and stream operations automatically reuse the same pool - Eliminates the need to pass
pool_nameon every ref operation
generate: :allmode: New library configuration option to generate wrappers for ALL public symbols in a Python module, not just those detected in your code- Use
{:mylib, "1.0.0", generate: :all}to generate complete bindings - Supports
submodulesoption for recursive module introspection - Full module introspection via
Introspector.introspect_module/2
- Use
- Context-aware type mapping:
TypeMappernow builds context from discovered classes and resolves type references to generated Elixir modulesTypeMapper.build_context/1creates a mapping of Python classes to Elixir modulesTypeMapper.with_context/2runs code with type resolution contextTypeMapper.to_spec/2resolves class types when context is available
- Parsed docstring support:
Generator.normalize_docstring/1handles both raw strings and parsed docstring maps from full module introspection - Multi-session example: New
examples/multi_session_exampledemonstrating concurrent isolated Python sessions - "multiple snakes in the pit"- Concurrent sessions with
Task.asyncand different session IDs - State isolation between sessions
- Parallel processing pattern with
Task.async_stream - Session-scoped object lifetime management
- Concurrent sessions with
- Multi-session documentation: README now includes "Multiple Snakes in the Pit" section explaining concurrent session patterns for multi-tenant apps, A/B testing, and parallel workers
- Affinity modes in examples:
multi_session_examplenow demonstrates hint vs strict queue vs strict fail-fast under load, per-call overrides, tainted worker handling, and streaming with session-bound refs - Affinity defaults example: New
examples/affinity_defaults_examplefor single-pool defaults and per-call overrides - Session affinity guide: Added
guides/SESSION_AFFINITY.mdwith routing semantics, errors, and streaming guidance - ConfigHelper affinity support:
SnakeBridge.ConfigHelperacceptsaffinityandpoolsto configure Snakepit routing defaults per pool
Changed
- Class methods now skip
self/clsparameters: Generated method signatures no longer include implicit Python instance/class parameters - Method deduplication: When introspection finds multiple signatures for the same method (e.g.,
__getitem__andgetmapping to the same Elixir name), only one is generated - Reserved word handling: Parameter names that are Elixir reserved words (e.g.,
and,or,not) are now prefixed withpy_(e.g.,py_and) - Variadic args signature: Functions with both
*argsandoptsno longer have conflicting defaults - Session cleanup logging is opt-in: Set
config :snakebridge, session_cleanup_log_level: :debugto log cleanup events; cleanup also emits[:snakebridge, :session, :cleanup]telemetry
Removed
- Hardcoded ML type mappings: Removed special-case handling for
numpy.ndarray,torch.Tensor,pandas.DataFrame, etc. These now use the generic class type system with context-aware resolution
Fixed
InspectandString.Charsprotocol implementations forSnakeBridge.Refnow catch:exiterrors in addition to exceptions, preventing crashes during ref inspection when the runtime is unavailable
Internal
- Refactored
RealPythonCasetest support for cleaner Python dependency resolution - Added test coverage for
generate: :all, context-aware type mapping, and self/cls parameter skipping - Added test coverage for pool_name propagation on ref operations
- Bridge client example now dynamically resolves gRPC address via Snakepit's
await_ready/1with fallback toSNAKEPIT_GRPC_ADDRESS/SNAKEPIT_GRPC_ADDRenv vars - Python bridge client demo prefers
SNAKEPIT_GRPC_ADDRESSover deprecatedSNAKEPIT_GRPC_ADDR - Test helper auto-generates unique
instance_nameandinstance_tokenfor test isolation with parallel partition support
0.8.1 - 2026-01-09
Changed
- Dev and test logger configs no longer pin
:consoleas the only backend. - Example lockfiles now record Elixir 1.19.4.
Internal
- Prefer direct list emptiness checks in generator, registry, and tests.
- Reuse a shared streaming chunk callback for clarity.
- Updated tooling/lockfile deps (supertester 0.5.1, credo 1.7.15, dialyxir 1.4.7, ex_doc 0.39.3, erlex 0.2.8).
0.8.0 - 2026-01-02
Added
SnakeBridge.run_as_script/2wrapper with safe defaults for Snakepit 0.9.0 exit semantics.- Script shutdown telemetry forwarder for
[:snakepit, :script, :shutdown, ...]events. - Integration tests covering script exit behavior and embedded usage.
Changed
- Examples now use
SnakeBridge.run_as_script/2instead of calling Snakepit directly. - Docs updated to describe
exit_mode/stop_modedefaults andSNAKEPIT_SCRIPT_EXIT.
Fixed
- Clarified script shutdown behavior to avoid unintended VM stops in embedded usage.
- Examples runner now starts applications during
mix runso session tracking is available.
Internal
- Added
supertester0.4.0 for robust test infrastructure. - Refactored flaky
Process.sleepcalls to use polling-basedeventually/2helper. - Test files now use proper process monitoring and message passing for synchronization.
- Split runtime responsibilities into Runtime.Payload, Runtime.SessionResolver, and Runtime.Streamer.
- Extracted compiler flow into Compiler.Pipeline and Compiler.IntrospectionRunner; Mix task now delegates.
- Split generator rendering into Generator.Function and Generator.Class with the Generator module coordinating output.
0.7.10 - 2026-01-01
Changed
- auto_install default changed from
:devto:dev_test- Python packages now auto-install during bothmix compileandmix test, eliminating the need to runmix snakebridge.setupbefore running tests - Added
:dev_testoption forauto_installsetting (matches both dev and test environments) - Environment variable
SNAKEBRIDGE_AUTO_INSTALLnow also acceptsdev_testvalue - Updated all examples to use the new default
Fixed
- First-run test experience -
mix testnow works out of the box without requiringmix snakebridge.setupfirst
0.7.9 - 2026-01-01
Added
- ConfigHelper: New
SnakeBridge.ConfigHelpermodule for zero-boilerplate snakepit configuration - Add
configure_snakepit!/1for runtime auto-configuration of Python venv, adapter, and PYTHONPATH - Add
snakepit_config/1for declarative config generation - Add
debug_config/0for troubleshooting venv detection
Changed
- All examples now use simplified
config/runtime.exswith ConfigHelper (replaces ~30 lines of boilerplate) - Updated README installation instructions to include runtime.exs setup
- ConfigHelper automatically follows symlinks to find venv in path dependencies
Fixed
- External projects using snakebridge as a dependency no longer need manual snakepit configuration
- Venv detection now works correctly for path deps, hex deps, and local development
0.7.8 - 2026-01-01
Changed
- Breaking: Python library configuration now uses
python_depsproject key instead of dependency tuple options - This fixes the "unknown options: :libraries" error when installing from Hex
- Configuration now mirrors how
deps/0works with a parallelpython_deps/0function generated_dirandmetadata_dirnow read from Application config instead of dependency options
Migration
Before (broken with Hex packages):
def deps do
[{:snakebridge, "~> 0.7.7", libraries: [...]}] # ERROR with Hex!
endAfter (works with all installation methods):
def project do
[
app: :my_app,
deps: deps(),
python_deps: python_deps()
]
end
defp deps do
[{:snakebridge, "~> 0.7.8"}]
end
defp python_deps do
[
{:numpy, "1.26.0"},
{:pandas, "2.0.0", include: ["DataFrame", "read_csv"]}
]
end0.7.7 - 2025-12-31
Added
SnakeBridge.Defaultsmodule centralizing all configurable values with documentation- Full configuration support for previously hardcoded values:
:introspector_timeout- Introspection timeout in ms (default: 30,000):introspector_max_concurrency- Max concurrent introspection tasks (default:System.schedulers_online()):pytorch_index_base_url- PyTorch wheel index URL for private mirrors (default:"https://download.pytorch.org/whl/"):cuda_thresholds- CUDA version to variant mapping, extensible for new CUDA versions:session_max_refs- Maximum refs per session (default: 10,000):session_ttl_seconds- Session TTL in seconds (default: 3,600)
Changed
- Introspector now supports both nested (
:introspector) and flat key configuration for backwards compatibility - CUDA variant fallback logic now uses configurable thresholds instead of hardcoded guards
- Session context defaults now read from application config, overridable per-call
- Runtime timeout architecture overhaul - fixes the 30s gRPC deadline problem that broke LLM calls:
- New default timeout: 120s (2 minutes) instead of 30s
- New default stream timeout: 30 minutes instead of 5 minutes
- Profile-based timeout system with built-in profiles:
:default,:streaming,:ml_inference,:batch_job - Per-library profile mapping via
runtime: [library_profiles: %{"transformers" => :ml_inference}] - All runtime calls now apply timeout defaults via
apply_runtime_defaults/3 __runtime__is now a documented first-class feature (not an undocumented passthrough)- Generated module docs now include Runtime Options section explaining timeout configuration
Runtime Timeout Architecture
The new timeout system addresses the "30s gRPC deadline kills LLM calls" problem by:
- Raising default timeout from 30s to 120s (2 minutes)
- Profile-based configuration - select
:ml_inferencefor 10-minute timeout,:batch_jobfor infinity - Per-call override via
__runtime__: [timeout: X]or__runtime__: [timeout_profile: :ml_inference] - Library-specific defaults - configure
transformersto always use:ml_inferenceprofile - Streaming-aware - streaming calls default to
:streamingprofile with 30-minute stream_timeout
Example configuration:
config :snakebridge,
runtime: [
timeout_profile: :default,
library_profiles: %{
"transformers" => :ml_inference,
"torch" => :batch_job
}
]Example per-call override:
Transformers.generate(prompt, __runtime__: [timeout_profile: :ml_inference])
Numpy.compute(data, __runtime__: [timeout: 600_000])0.7.6 - 2025-12-31
Added
- First-class ref lifecycle errors:
RefNotFoundError,SessionMismatchError,InvalidRefError - Error translator now converts Python ref errors to structured Elixir exceptions
- Introspection failures now logged with details during normal mode compilation
- Telemetry event
[:snakebridge, :introspection, :error]emitted for introspection failures - Introspection summary displayed after compilation if errors occurred
SnakeBridge.release_ref/1,2delegates for explicit ref cleanupSnakeBridge.release_session/1,2delegates for session cleanup
Fixed
set_attr@spec and documentation now correctly show{:ok, term()}return type- TTL documentation now consistently states: disabled by default (env var), SessionContext default 3600s
- Introspection errors no longer silently swallowed in normal mode compilation
- Users now see which symbols failed to introspect and why
- Session ID consistency across all Runtime call paths -
__runtime__: [session_id: X]now respected by:call/4with atom modulesget_module_attr/3with both atom and string modulescall_class/4call_helper/3(both list and map opts variants)call_method/4(runtime opts now override ref's embedded session)stream/5with atom modules
- All call paths now use
resolve_session_id()for consistent priority: runtime_opts > ref > context > auto-session
Removed
- Pre-populated
priv/snakebridge/registry.jsonfiles from repository; registry is now generated per-project duringmix compile
Changed
- Added
priv/snakebridge/registry.jsonto.gitignoreto prevent tracking of generated registry artifacts - Release validation against Snakepit 0.8.7 with passing Elixir/Python test suites and dialyzer
0.7.5 - 2025-12-30
Added
- Python BridgeClient for gRPC execution with streaming support
- Streaming client decoding for ToolChunk payloads (JSON + raw bytes) with metadata and chunk ids
- Correlation ID propagation via
x-snakepit-correlation-idmetadata header - Any encoding compatibility for tool parameters (raw JSON bytes)
- Streaming client tests covering RPC selection, headers, decoding, and binary parameter validation
bridge_client_exampledemonstrating Python BridgeClient usage against the Elixir BridgeServer
Changed
- README streaming documentation clarifies server-side streaming RPC usage and
supports_streamingrequirement
Fixed
- Ensure session affinity is always passed to Snakepit pool for ref-based operations
- Preserve boolean results in Python gRPC response type inference
- Stream iteration now prefers
__iter__before falling back to__next__
0.7.4 - 2025-12-30
Added
SnakeBridge.Bytesstruct for explicit binary data encoding to PythonbytesSnakeBridge.SerializationErrorexception for unsupported type encoding- Tagged dict wire format for maps with non-string keys (integers, tuples, etc.)
- Auto-session: BEAM processes automatically get session IDs without explicit
with_session/1 SnakeBridge.Runtime.current_session/0to get current session IDSnakeBridge.Runtime.release_auto_session/0for explicit session cleanupSnakeBridge.Runtime.clear_auto_session/0for testingSnakeBridge.SessionManager.unregister_session/1to unregister without Python-side release- Universal FFI:
SnakeBridge.call/4now accepts string module paths for dynamic Python calls - Universal FFI:
SnakeBridge.stream/5accepts string module paths - Universal FFI:
SnakeBridge.get/3for module attributes with string paths SnakeBridge.call!/4,SnakeBridge.get!/3,SnakeBridge.method!/4,SnakeBridge.attr!/3bang variantsSnakeBridge.method/4as alias forDynamic.call/4SnakeBridge.attr/3as alias forDynamic.get_attr/3SnakeBridge.set_attr/4as alias forDynamic.set_attr/4SnakeBridge.bytes/1convenience function for creating Bytes wrappersSnakeBridge.ref?/1to check if a value is a Python refSnakeBridge.Runtime.stream_dynamic/5for streaming with string module paths- Python adapter:
_is_json_safe()validation function as safety net for encode results - New
universal_ffi_exampleshowcasing all Universal FFI features
Changed
- Updated
dynamic_dispatch_examplewith new convenience APIs (SnakeBridge.call/4, etc.) - Updated
types_showcasewithSnakeBridge.Bytesand non-string key map examples - Updated
session_lifecycle_examplewith auto-session demonstration - Encoder now raises
SerializationErrorinstead of silently callinginspect/1on unknown types - Maps with non-string keys are now encoded as tagged dicts with key-value pairs
- Session ID is now always included in wire payloads (auto-generated if not explicit)
SnakeBridge.call/4now dispatches tocall_dynamic/4when given string module path- Python adapter now unconditionally ref-wraps non-JSON-serializable return values
- Improved Python
encode()to explicitly mark unencodable values with__needs_ref__ - Added JSON safety validation in
encode_result()as a safety net
Fixed
- Maps with integer/tuple keys now serialize correctly instead of coercing keys to strings
- Memory leaks from refs in "default" session when not using explicit
SessionContext - Ref collisions between unrelated Elixir processes
- Lists/dicts containing non-serializable items now properly return refs
- Eliminated partial/lossy encoding of complex Python objects
0.7.3 - 2025-12-30
Added
- Universal FFI Core:
SnakeBridge.Dynamicmodule for method dispatch (call,get_attr,set_attr) on un-generated refs.SnakeBridge.Runtime.call_dynamic/4to invoke arbitrary Python functions without compile-time scanning.SnakeBridge.ModuleResolverto automatically disambiguate classes from submodules during compilation.SnakeBridge.SessionManagerandSessionContextfor process-bound Python object lifecycle management.SnakeBridge.StreamRefimplementingEnumerablefor lazy iteration over Python generators/iterators.SnakeBridge.WithContext.with_python/2macro for Python context manager (withstatement) support.SnakeBridge.CallbackRegistryto pass Elixir functions into Python as callable callbacks.
- Protocol Integration:
Inspect,String.Chars, andEnumerableprotocol implementations forSnakeBridge.Ref.SnakeBridge.DynamicExceptionto automatically map unknown Python exceptions to Elixir structs.SnakeBridge.Runtime.get_module_attr/3for accessing module-level constants and objects.
- Configuration:
config/wheel_variants.jsonsupport viaSnakeBridge.WheelConfigfor externalized PyTorch wheel selection.SNAKEBRIDGE_ATOM_CLASSenvironment variable to opt-in to legacyAtomobject wrapping.
Changed
- Type System:
- Python atom decoding now defaults to plain strings for compatibility with standard libraries.
- Unknown Python return types now automatically return a Ref handle instead of a string representation.
- Runtime boundary now enforces strict encoding/decoding of all arguments and results.
- Signature Model:
- Manifest now supports arity ranges (min/max) to correctly match calls with optional arguments.
- C-extension functions (without inspectable signatures) now generate variadic wrappers (up to 8 args).
- Required keyword-only arguments are now validated at runtime with clear error messages.
- Function and method names are sanitised (e.g.,
class->py_class) while preserving Python mapping.
- Introspection:
- Unified introspection logic into a standalone
priv/python/introspect.pyscript (removed embedded string script). - Introspection now captures module attributes and detects protocol-supporting dunder methods.
- Unified introspection logic into a standalone
- Telemetry:
- Standardized compile events under
[:snakebridge, :compile, phase, :start|:stop]. - Unified metadata schema for compile events (
library,phase,details).
- Standardized compile events under
- Python Adapter:
- Added thread locks to global registries for concurrency safety.
- Implemented session-scoped cleanup to prevent memory leaks on Elixir process exit.
Fixed
- Manifest correctness: Fixed mismatch where scanner reported call-site arity while manifest stored required arity, causing perpetual "missing" symbols.
- File Generation: Fixed potential race conditions in
write_if_changedusing global locking. - Registry:
SnakeBridge.Registryis now properly populated and saved during the compilation phase. - Lockfile: Generator hash now reflects actual source content rather than just the version string.
0.7.2 - 2025-12-29
Added
- Wire schema version tagging and atom tags across Elixir/Python with tolerant decoding for legacy keys
- Protocol version markers on runtime payloads with adapter compatibility checks
SnakeBridge.Refschema plusrelease_ref/release_sessionruntime helperserror_modeconfiguration for translated or raised runtime errors- Ref registry TTL/LRU controls in the Python adapter (
SNAKEBRIDGE_REF_TTL_SECONDS,SNAKEBRIDGE_REF_MAX) - Telemetry emission for scan, introspection, generate, docs fetch, and lock verification
protocol_payload/0andnormalize_args_opts/2helpers inSnakeBridge.Runtimefor runtime payload consistency- Example failure tracking helper to hard-fail on unexpected demo errors
SNAKEBRIDGE_ALLOW_LEGACY_PROTOCOLtoggle for protocol compatibility checks
Changed
- Manifest symbol keys normalized without
Elixir.prefixes and migrated on load - Strict verification now validates class modules, methods, and attributes via AST parsing
- Dotted python library roots handled consistently in submodule generation and runtime metadata
- Generated wrappers accept extra positional args via
args \\ []and emit typed specs - Helper generation now writes only when content changes; Mix compiler reports manifest/lock artifacts
- Protocol compatibility is strict by default; legacy payloads require
SNAKEBRIDGE_ALLOW_LEGACY_PROTOCOL=1 - Generated wrappers normalize keyword lists passed as args into opts when opts are omitted
- Example runners now raise on Snakepit script failures instead of printing and continuing
- Docs showcase fetches docstrings via the Python runner instead of raw runtime payloads
Fixed
- Async scan/introspection task failures now surface structured errors instead of crashing
- Snakepit lockfile now aligns with the declared
~> 0.8.3requirement - Example payloads include protocol metadata to avoid version mismatches
- Wrapper/streaming/class constructor examples no longer fail JSON encoding for keyword options
0.7.1 - 2025-12-29
Changed
- Upgrade snakepit dependency from 0.8.1 to 0.8.2
- Example runner (
run_all.sh) now updates all dependencies upfront before running examples - Example runner now fails fast on compilation errors instead of silently continuing
Fixed
- Telemetry event paths updated from
[:snakepit, :call, :*]to[:snakepit, :python, :call, :*]to align with snakepit 0.8.2 - RuntimeForwarder test setup now handles already-attached handlers gracefully
0.7.0 - 2025-12-28
Added
- Wrapper argument surface fix: All generated wrappers now accept
opts \\ []for runtime flags (idempotent,__runtime__,__args__) and Python kwargs - Streaming generation: Functions in
streaming:config now generate*_streamvariants with proper@spec - Strict mode verification: Now verifies generated files exist and contain expected functions
- Documentation pipeline: Docstrings are converted from RST/NumPy/Google style to ExDoc Markdown
- Telemetry emission: Compile pipeline now emits
[:snakebridge, :compile, :start|:stop|:exception]events
Changed
- Class constructors now match Python
__init__signatures instead of hardcodednew(arg, opts) - File writes use atomic temp files with unique names for concurrency safety
- File writes skip when content unchanged (no more mtime churn)
Fixed
- Functions with
POSITIONAL_OR_KEYWORDdefaulted parameters now accept opts VAR_POSITIONALparameters are now recognized for opts enablement- Classes with 0, 2+, or optional
__init__args now construct correctly
Developer Experience
- New examples:
wrapper_args_example,class_constructor_example,streaming_example,strict_mode_example - Updated
run_all.shwith new examples
0.6.0 - 2025-12-27
Added
Docs.RstParserfor Google, NumPy, Sphinx, and Epytext docstring parsingMarkdownConverterto transform parsed docstrings into ExDoc markdown with type mappingsMathRendererfor reStructuredText math directives (:math:,.. math::) to KaTeX formatErrorTranslatorfor structured Python exception translation with actionable suggestionsShapeMismatchErrorfor tensor dimension parsing with transposition/broadcasting guidanceOutOfMemoryErrorfor device memory stats and recovery steps (CUDA, MPS)DtypeMismatchErrorfor precision conflict detection with casting guidance:telemetryinstrumentation for scan, introspect, generate, and runtime callsRuntimeForwarderto enrich Snakepit execution events with SnakeBridge contextHandlers.LoggerandHandlers.Metricsfor compilation timing logs and standard metrics- Hardware identity in
snakebridge.lock(accelerator, CUDA version, GPU count, CPU features) mix snakebridge.verifyto validate lock files against current runtime hardwareWheelSelectorfor hardware-specific PyTorch wheel resolution (cu118, cu121, rocm, cpu)examples/with 8 demo projects covering docs, types, errors, telemetry, and performancebenchmarks/compile_time_benchmark.exsfor scan and generation performance tracking
Changed
TypeMappernow emits specific typespecs fornumpy.ndarray,torch.Tensor, andpandas.DataFrame- Suppress noisy OTLP/TLS logs during application startup
Dependencies
- Requires snakepit ~> 0.8.1
0.5.0 - 2025-12-25
Added
SnakeBridge.PythonEnvmodule for Python environment orchestrationSnakeBridge.EnvironmentErrorfor missing package errorsSnakeBridge.IntrospectionErrorfor classified introspection failuresmix snakebridge.setuptask for provisioning Python packages- Config options:
pypi_package,extrasper library Config option:
auto_install(:never | :dev | :always)- Strict mode enforcement via
SNAKEBRIDGE_STRICT=1orstrict: true - Package identity in
snakebridge.lock(python_packages,python_packages_hash)
Changed
- Compiler now calls
PythonEnv.ensure!/1before introspection (when not strict) - Improved introspection error messages with fix suggestions
Dependencies
- Requires snakepit ~> 0.7.5 (for PythonPackages support)
0.4.0 - 2025-12-25
Added
- Compile-time pre-pass pipeline (scan -> introspect -> generate) with manifest + lockfile.
- Deterministic source output: one file per library under
lib/snakebridge_generated/*.ex, committed to git. - Snakepit-aligned runtime helpers (
snakebridge.call/snakebridge.stream) and runtime client override for tests. - Snakepit-backed Python execution for introspection and docs.
- Lockfile environment identity recorded from Snakepit runtime (version, platform, hash).
- Discovery metadata for classes and submodules, including deterministic regeneration.
- Example projects updated/added for v3 (
examples/math_demo,examples/proof_pipeline).
Changed
- Library configuration now lives in dependency options (
mix.exs), notconfig/*.exs. Generated specs return
{:ok, term()} | {:error, Snakepit.Error.t()}and useSnakepit.PyRef/Snakepit.ZeroCopyRef.- Doc search now ranks results using discovery metadata.
- Runtime behavior is fully delegated to Snakepit; SnakeBridge stays compile-time only.
Removed
- Legacy v2 mix tasks (
snakebridge.gen,list,info,clean,remove) and registry-driven CLI flow. - Multi-file adapter output under
lib/snakebridge/adapters/. - Auto-gitignore behavior for generated bindings.
0.3.2 - 2025-12-23
Added
- Zero-friction Python integration with auto venv/pip
- Automated adapter creation with
mix snakebridge.adapter.create
0.3.1 - 2025-12-23
Changed
- Manifest as single source of truth
- Bridges relocated to
priv/python/bridges/
0.3.0 - 2025-12-23
Added
- Manifest-driven workflow with mix tasks
- Built-in manifests for sympy, pylatexenc, math-verify
- Snakepit auto-start launcher
0.2.0 - 2025-10-26
Added
- Live Python integration with generic adapter
- Public API: discover, generate, integrate
- Mix tasks for discovery and generation
0.1.0 - 2025-10-25
Added
- Initial release
- Core configuration schema
- Type system mapper
- Basic code generation