Metadata-Version: 2.5
Name: funcwire
Version: 0.1.0
Summary: Provider-neutral, validated contracts for typed Python callables.
Project-URL: Changelog, https://github.com/BhuvaneshN09/funcwire/blob/main/CHANGELOG.md
Project-URL: Documentation, https://github.com/BhuvaneshN09/funcwire#readme
Project-URL: Issues, https://github.com/BhuvaneshN09/funcwire/issues
Project-URL: Source, https://github.com/BhuvaneshN09/funcwire
Author: FuncWire contributors
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: callables,contracts,function-calling,json-schema,mcp,rpc,tool-calling,validation,workflow
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: typing-extensions>=4.4; extra == 'dev'
Description-Content-Type: text/markdown

# FuncWire

[![PyPI](https://img.shields.io/pypi/v/funcwire?label=pypi&color=2496ED)](https://pypi.org/project/funcwire/)
[![Python](https://img.shields.io/pypi/pyversions/funcwire?label=python&color=2496ED)](https://pypi.org/project/funcwire/)
[![License](https://img.shields.io/badge/license-Apache--2.0-78BE20)](https://github.com/BhuvaneshN09/funcwire/blob/main/LICENSE)
[![CI](https://github.com/BhuvaneshN09/funcwire/actions/workflows/ci.yml/badge.svg)](https://github.com/BhuvaneshN09/funcwire/actions/workflows/ci.yml)

**Portable contracts for Python callables.**

FuncWire is a zero-dependency contract runtime for typed Python callables. It converts ordinary
functions, bound methods, and callable objects into deterministic, machine-readable contracts with
JSON Schema, strict recursive validation, annotation-directed codecs, exact Python signature
binding, synchronous and asynchronous invocation, return contracts, and provider adapters.

Designed as infrastructure rather than framework glue, FuncWire gives RPC systems, workflow
engines, plugin platforms, remote workers, interface generators, MCP servers, and LLM tool-calling
stacks one stable intermediate representation for Python callable boundaries.

### Engineering guarantees

- **Provider-neutral core:** no OpenAI, Anthropic, Gemini, MCP, or orchestration assumptions in the IR.
- **Exact signature semantics:** positional-only, keyword-only, defaults, `*args`, and `**kwargs`.
- **Strict by default:** external strings are not silently coerced into numbers or booleans.
- **Structured codecs:** dataclasses, TypedDicts, enums, UUIDs, paths, dates, times, and decimals.
- **Deterministic contracts:** modern JSON Schema, recursive `$defs`/`$ref`, and versioned serialization.
- **Small dependency surface:** no runtime dependencies and no provider SDK imports.
- **Operational clarity:** structured errors, sync/async separation, security guidance, and typed APIs.

> FuncWire is an alpha. The v1 serialized contract is versioned, but the Python API may evolve
> before 1.0.

## Install

```bash
pip install funcwire
```

Python 3.10 or newer is required.

## First contract

```python
from typing import Literal
from funcwire import spec


def weather(city: str, units: Literal["c", "f"] = "c") -> dict[str, float]:
    """Get current weather.

    Args:
        city: City to look up.
        units: Temperature unit.
    """
    return {"temperature": 21.0}


tool = spec(weather)

print(tool.name)
print(tool.input_schema)
values = tool.validate({"city": "Toronto"})
result = tool.call({"city": "Toronto", "units": "c"})
openai_tool = tool.export("openai")
mcp_tool = tool.export("mcp")
```

`validate()` is strict: `"21"` is not accepted for `int` or `float`. Decoding only happens when
the annotation justifies it; for example, a UUID string becomes `uuid.UUID`, and an object matching
a dataclass becomes that dataclass.

## Annotated constraints

```python
from typing import Annotated
from funcwire import Description, Max, Min, spec


def set_speed(
    speed: Annotated[float, Description("Motor speed"), Min(0), Max(1)],
) -> None: ...


speed = spec(set_speed)
speed.validate({"speed": 0.5})
```

`Description`, `Min`, `Max`, `MinLength`, `MaxLength`, `Pattern`, and `Examples` map directly to
JSON Schema concepts and are enforced locally where applicable.

## Signatures and invocation

FuncWire supports positional-only parameters, positional-or-keyword parameters, keyword-only
parameters, defaults, `*args`, and `**kwargs`. External calls are always objects. The keys for
variadic parameters contain an array and object respectively:

```python
def invoke(a: int, /, *items: str, enabled: bool, **labels: float):
    return a, items, enabled, labels


tool = spec(invoke)
tool.call(
    {
        "a": 1,
        "items": ["x", "y"],
        "enabled": True,
        "labels": {"score": 0.8},
    }
)
```

Calling `.call()` on an async function raises `InvocationError`. Use `await tool.acall(data)`.
`acall()` can also invoke a synchronous callable without using a thread or executor.

## Supported annotations

- `str`, `int`, `float`, `bool`, `None`, and `Any`
- `list`, `tuple`, `set`, `frozenset`, and string-keyed `dict`
- `Sequence`, `Iterable`, `Mapping`, `Optional`, `Union`, `Literal`, and `Annotated`
- modern `|` unions, resolvable aliases, bounded/constrained `TypeVar`
- dataclasses, nested/recursive dataclasses, `TypedDict`, and `Enum`
- `datetime`, `date`, `time`, `Path`, `UUID`, and `Decimal`

Unresolvable forward references, non-string mapping keys, unsupported classes, non-scalar enum or
literal values, and unknown `Annotated` metadata fail explicitly. Arbitrary classes are not guessed.

## Custom types

```python
from funcwire import register_type

register_type(
    MyType,
    schema={"type": "string", "format": "my-type"},
    validator=lambda value: isinstance(value, str),
    decoder=MyType.parse,
    encoder=str,
)
```

Registration is atomic and concurrent reads use a lock-protected registry. A newly built contract
captures schema metadata, while validation and codecs consult the current registry. Register types
during application startup for stable behavior. `unregister_type(MyType)` is intended primarily for
tests and plugin lifecycle management.

## Provider adapters

`tool.export("openai")`, `tool.export("anthropic")`, `tool.export("gemini")`, and
`tool.export("mcp")` return plain dictionaries. No provider SDK is imported or required. These are
metadata transformations; the universal `FuncSpec` remains provider-neutral.

## Results, serialization, and compatibility

Use `validate_result()` for external result data and `encode_result()` for Python results. `to_dict()`
and `to_json()` serialize only the contract description, tagged with `funcwire_version: 1`; executable
callables are deliberately not serialized. `diff(old, new)` reports conservative compatibility
changes to parameters and returns.

## Validation is not a sandbox

`tool.call(data)` executes the wrapped Python callable normally with the process's permissions.
Validation establishes data shape and type; it does not make untrusted code safe, authorize an
operation, limit resource use, or isolate side effects. Provider exports provide no security boundary.

## Limitations

- Callable reconstruction from serialized contracts is intentionally unsupported.
- Generator functions, overloaded dispatch semantics, protocols, arbitrary user classes, and
  provider-specific strict-schema subsets do not receive special handling yet.
- `Decimal` uses a JSON string to avoid precision loss.
- Abstract sequences decode to `list`; abstract mappings decode to `dict`.
- Compatibility analysis is conservative; type changes are classified as potentially breaking
  rather than attempting formal schema subsumption.

See [the architecture](docs/design/architecture.md),
[competitive analysis](docs/design/competitive-analysis.md), and [release audit](RELEASE_AUDIT.md).

## Development

```bash
python -m venv .venv
.venv/bin/pip install -e '.[dev]'
.venv/bin/pytest --cov=funcwire --cov-branch
.venv/bin/ruff check .
.venv/bin/mypy src
.venv/bin/python -m build
```

Contributions are welcome under the Apache License 2.0. See [CONTRIBUTING.md](CONTRIBUTING.md).
