fireweave 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. fireweave-0.1.0/LICENSE +21 -0
  2. fireweave-0.1.0/PKG-INFO +97 -0
  3. fireweave-0.1.0/README.md +62 -0
  4. fireweave-0.1.0/pyproject.toml +59 -0
  5. fireweave-0.1.0/setup.cfg +4 -0
  6. fireweave-0.1.0/src/fireweave/__init__.py +126 -0
  7. fireweave-0.1.0/src/fireweave/_version.py +5 -0
  8. fireweave-0.1.0/src/fireweave/adapters/__init__.py +23 -0
  9. fireweave-0.1.0/src/fireweave/adapters/base.py +117 -0
  10. fireweave-0.1.0/src/fireweave/adapters/local.py +76 -0
  11. fireweave-0.1.0/src/fireweave/adapters/memory.py +115 -0
  12. fireweave-0.1.0/src/fireweave/adapters/posthog.py +426 -0
  13. fireweave-0.1.0/src/fireweave/adapters/remote.py +310 -0
  14. fireweave-0.1.0/src/fireweave/aio.py +176 -0
  15. fireweave-0.1.0/src/fireweave/capabilities.py +59 -0
  16. fireweave-0.1.0/src/fireweave/client.py +809 -0
  17. fireweave-0.1.0/src/fireweave/config.py +99 -0
  18. fireweave-0.1.0/src/fireweave/context.py +199 -0
  19. fireweave-0.1.0/src/fireweave/decision.py +42 -0
  20. fireweave-0.1.0/src/fireweave/errors.py +255 -0
  21. fireweave-0.1.0/src/fireweave/openfeature/__init__.py +22 -0
  22. fireweave-0.1.0/src/fireweave/openfeature/local_provider.py +211 -0
  23. fireweave-0.1.0/src/fireweave/openfeature/provider.py +163 -0
  24. fireweave-0.1.0/src/fireweave/py.typed +0 -0
  25. fireweave-0.1.0/src/fireweave/runtime.py +413 -0
  26. fireweave-0.1.0/src/fireweave/types.py +34 -0
  27. fireweave-0.1.0/src/fireweave.egg-info/PKG-INFO +97 -0
  28. fireweave-0.1.0/src/fireweave.egg-info/SOURCES.txt +42 -0
  29. fireweave-0.1.0/src/fireweave.egg-info/dependency_links.txt +1 -0
  30. fireweave-0.1.0/src/fireweave.egg-info/requires.txt +11 -0
  31. fireweave-0.1.0/src/fireweave.egg-info/top_level.txt +1 -0
  32. fireweave-0.1.0/tests/test_client_extensions.py +374 -0
  33. fireweave-0.1.0/tests/test_concurrency.py +159 -0
  34. fireweave-0.1.0/tests/test_conformance.py +26 -0
  35. fireweave-0.1.0/tests/test_context.py +150 -0
  36. fireweave-0.1.0/tests/test_control_points.py +46 -0
  37. fireweave-0.1.0/tests/test_errors.py +127 -0
  38. fireweave-0.1.0/tests/test_lifecycle.py +201 -0
  39. fireweave-0.1.0/tests/test_local_provider.py +154 -0
  40. fireweave-0.1.0/tests/test_openfeature_provider.py +164 -0
  41. fireweave-0.1.0/tests/test_posthog_adapter.py +416 -0
  42. fireweave-0.1.0/tests/test_register_target.py +142 -0
  43. fireweave-0.1.0/tests/test_remote_adapter.py +61 -0
  44. fireweave-0.1.0/tests/test_runtime_evaluation.py +87 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fireweave AI, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,97 @@
1
+ Metadata-Version: 2.4
2
+ Name: fireweave
3
+ Version: 0.1.0
4
+ Summary: Fireweave polyglot SDK for Python — OpenFeature-compatible, server-first flag evaluation and release-safety extensions (spec v0.1.0).
5
+ Author: Fireweave AI, Inc.
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/FireWeave-HQ/fireweave-sdk
8
+ Project-URL: Documentation, https://github.com/FireWeave-HQ/fireweave-sdk/tree/master/docs
9
+ Project-URL: Repository, https://github.com/FireWeave-HQ/fireweave-sdk
10
+ Project-URL: Issues, https://github.com/FireWeave-HQ/fireweave-sdk/issues
11
+ Project-URL: Changelog, https://github.com/FireWeave-HQ/fireweave-sdk/blob/master/CHANGELOG.md
12
+ Project-URL: Source, https://github.com/FireWeave-HQ/fireweave-sdk/tree/master/sdks/python
13
+ Keywords: feature-flags,openfeature,posthog,rollout,release
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Provides-Extra: posthog
27
+ Requires-Dist: posthog==7.31.0; extra == "posthog"
28
+ Provides-Extra: openfeature
29
+ Requires-Dist: openfeature-sdk<0.11,>=0.10.0; extra == "openfeature"
30
+ Provides-Extra: dev
31
+ Requires-Dist: posthog==7.31.0; extra == "dev"
32
+ Requires-Dist: openfeature-sdk<0.11,>=0.10.0; extra == "dev"
33
+ Requires-Dist: pytest>=8; extra == "dev"
34
+ Dynamic: license-file
35
+
36
+ # fireweave (Python SDK)
37
+
38
+ Fireweave polyglot SDK for Python — OpenFeature-compatible, server-first flag
39
+ evaluation with release-safety extensions (spec v0.1.0).
40
+
41
+ - **Core**: zero runtime dependencies (`fireweave` + `InMemoryAdapter`).
42
+ - **Extras**: `fireweave[posthog]` (PostHog backend, `posthog==7.31.0`),
43
+ `fireweave[openfeature]` (OpenFeature provider, `openfeature-sdk>=0.10,<0.11`).
44
+
45
+ ## Quick start (offline, in-memory)
46
+
47
+ ```python
48
+ from fireweave import FireweaveClient, FireweaveRuntime, InMemoryAdapter
49
+
50
+ adapter = InMemoryAdapter({
51
+ "new-checkout": {"type": "boolean", "enabled": True, "variant": "on", "value": True},
52
+ })
53
+ runtime = FireweaveRuntime(adapter)
54
+ runtime.initialize()
55
+ client = FireweaveClient(runtime)
56
+
57
+ assert client.flags.get_boolean_value("new-checkout", False) is True
58
+ client.shutdown()
59
+ ```
60
+
61
+ ## OpenFeature
62
+
63
+ ```python
64
+ from openfeature import api
65
+ from fireweave import FireweaveRuntime, InMemoryAdapter
66
+ from fireweave.openfeature import FireweaveProvider
67
+
68
+ api.set_provider(FireweaveProvider(FireweaveRuntime(InMemoryAdapter({...}))))
69
+ client = api.get_client()
70
+ client.get_boolean_value("new-checkout", False)
71
+ ```
72
+
73
+ ## PostHog backend
74
+
75
+ ```python
76
+ from fireweave import FireweaveConfig, FireweaveRuntime, FireweaveClient
77
+ from fireweave.adapters.posthog import PostHogAdapter
78
+
79
+ config = FireweaveConfig(project_api_key="phc_...", host="https://us.i.posthog.com")
80
+ runtime = FireweaveRuntime(PostHogAdapter(config=config), config)
81
+ runtime.initialize(backend_required=True)
82
+ client = FireweaveClient(runtime)
83
+ ```
84
+
85
+ ## Sync / async
86
+
87
+ The core is a thread-safe **sync** runtime; asyncio servers use
88
+ `fireweave.aio.AsyncFireweaveClient`, which offloads calls via
89
+ `asyncio.to_thread`. See `docs/adr/0004` and the module docstring.
90
+
91
+ ## Development
92
+
93
+ ```bash
94
+ python -m venv .venv && .venv/bin/pip install -e '.[dev]'
95
+ .venv/bin/pytest # unit + conformance tests
96
+ .venv/bin/python conformance/run_conformance.py # normalized results JSON
97
+ ```
@@ -0,0 +1,62 @@
1
+ # fireweave (Python SDK)
2
+
3
+ Fireweave polyglot SDK for Python — OpenFeature-compatible, server-first flag
4
+ evaluation with release-safety extensions (spec v0.1.0).
5
+
6
+ - **Core**: zero runtime dependencies (`fireweave` + `InMemoryAdapter`).
7
+ - **Extras**: `fireweave[posthog]` (PostHog backend, `posthog==7.31.0`),
8
+ `fireweave[openfeature]` (OpenFeature provider, `openfeature-sdk>=0.10,<0.11`).
9
+
10
+ ## Quick start (offline, in-memory)
11
+
12
+ ```python
13
+ from fireweave import FireweaveClient, FireweaveRuntime, InMemoryAdapter
14
+
15
+ adapter = InMemoryAdapter({
16
+ "new-checkout": {"type": "boolean", "enabled": True, "variant": "on", "value": True},
17
+ })
18
+ runtime = FireweaveRuntime(adapter)
19
+ runtime.initialize()
20
+ client = FireweaveClient(runtime)
21
+
22
+ assert client.flags.get_boolean_value("new-checkout", False) is True
23
+ client.shutdown()
24
+ ```
25
+
26
+ ## OpenFeature
27
+
28
+ ```python
29
+ from openfeature import api
30
+ from fireweave import FireweaveRuntime, InMemoryAdapter
31
+ from fireweave.openfeature import FireweaveProvider
32
+
33
+ api.set_provider(FireweaveProvider(FireweaveRuntime(InMemoryAdapter({...}))))
34
+ client = api.get_client()
35
+ client.get_boolean_value("new-checkout", False)
36
+ ```
37
+
38
+ ## PostHog backend
39
+
40
+ ```python
41
+ from fireweave import FireweaveConfig, FireweaveRuntime, FireweaveClient
42
+ from fireweave.adapters.posthog import PostHogAdapter
43
+
44
+ config = FireweaveConfig(project_api_key="phc_...", host="https://us.i.posthog.com")
45
+ runtime = FireweaveRuntime(PostHogAdapter(config=config), config)
46
+ runtime.initialize(backend_required=True)
47
+ client = FireweaveClient(runtime)
48
+ ```
49
+
50
+ ## Sync / async
51
+
52
+ The core is a thread-safe **sync** runtime; asyncio servers use
53
+ `fireweave.aio.AsyncFireweaveClient`, which offloads calls via
54
+ `asyncio.to_thread`. See `docs/adr/0004` and the module docstring.
55
+
56
+ ## Development
57
+
58
+ ```bash
59
+ python -m venv .venv && .venv/bin/pip install -e '.[dev]'
60
+ .venv/bin/pytest # unit + conformance tests
61
+ .venv/bin/python conformance/run_conformance.py # normalized results JSON
62
+ ```
@@ -0,0 +1,59 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "fireweave"
7
+ version = "0.1.0"
8
+ description = "Fireweave polyglot SDK for Python — OpenFeature-compatible, server-first flag evaluation and release-safety extensions (spec v0.1.0)."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Fireweave AI, Inc." }]
14
+ keywords = ["feature-flags", "openfeature", "posthog", "rollout", "release"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Typing :: Typed",
25
+ ]
26
+ # Core has NO runtime dependencies: the OpenFeature provider and the PostHog
27
+ # adapter are optional integrations behind extras. `import fireweave` plus the
28
+ # InMemory adapter work with the standard library only.
29
+ dependencies = []
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/FireWeave-HQ/fireweave-sdk"
33
+ Documentation = "https://github.com/FireWeave-HQ/fireweave-sdk/tree/master/docs"
34
+ Repository = "https://github.com/FireWeave-HQ/fireweave-sdk"
35
+ Issues = "https://github.com/FireWeave-HQ/fireweave-sdk/issues"
36
+ Changelog = "https://github.com/FireWeave-HQ/fireweave-sdk/blob/master/CHANGELOG.md"
37
+ "Source" = "https://github.com/FireWeave-HQ/fireweave-sdk/tree/master/sdks/python"
38
+
39
+ [project.optional-dependencies]
40
+ # ADR-0002 pin: PostHog snapshot API (`evaluate_flags`) — exact pin to absorb
41
+ # the in-flight /flags migration.
42
+ posthog = ["posthog==7.31.0"]
43
+ # Pre-1.0 OpenFeature SDK: pin below 0.11 (decision brief §3 / ADR-0003).
44
+ openfeature = ["openfeature-sdk>=0.10.0,<0.11"]
45
+ dev = [
46
+ "posthog==7.31.0",
47
+ "openfeature-sdk>=0.10.0,<0.11",
48
+ "pytest>=8",
49
+ ]
50
+
51
+ [tool.setuptools.packages.find]
52
+ where = ["src"]
53
+
54
+ [tool.setuptools.package-data]
55
+ fireweave = ["py.typed"]
56
+
57
+ [tool.pytest.ini_options]
58
+ testpaths = ["tests"]
59
+ addopts = "-q"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,126 @@
1
+ """Fireweave SDK for Python (spec v0.1.0).
2
+
3
+ Core installs with zero runtime dependencies. Optional extras:
4
+
5
+ - ``fireweave[posthog]`` — PostHog backend adapter (``fireweave.adapters.posthog``)
6
+ - ``fireweave[openfeature]`` — OpenFeature provider (``fireweave.openfeature``)
7
+
8
+ Quick start (in-memory, offline)::
9
+
10
+ from fireweave import FireweaveClient, FireweaveRuntime, InMemoryAdapter
11
+
12
+ adapter = InMemoryAdapter({"my-flag": {"type": "boolean", "enabled": True,
13
+ "variant": "on", "value": True}})
14
+ runtime = FireweaveRuntime(adapter)
15
+ runtime.initialize()
16
+ client = FireweaveClient(runtime)
17
+ client.control_points.get_boolean_value("my-flag", False)
18
+ client.shutdown()
19
+
20
+ There are no hidden global clients: everything is constructed explicitly and
21
+ injectable for tests.
22
+ """
23
+
24
+ from ._version import SPEC_VERSION, __version__
25
+ from .adapters import (
26
+ BackendAdapter,
27
+ FlagResolution,
28
+ FireweaveLocalAdapter,
29
+ FireweaveRemoteAdapter,
30
+ InMemoryAdapter,
31
+ RegisterTargetOptions,
32
+ RegisterTargetResult,
33
+ )
34
+ from .capabilities import CANONICAL_CAPABILITIES, CapabilityRegistry
35
+ from .client import (
36
+ CapabilityResult,
37
+ ExposureResult,
38
+ FireweaveClient,
39
+ FlushResult,
40
+ ReleaseContext,
41
+ ReleaseResult,
42
+ SignalResult,
43
+ )
44
+ from .config import DEFAULT_ALLOWED_HOSTS, FireweaveConfig
45
+ from .context import ContextLimits, EvaluationContext, merge_contexts, validate_context
46
+ from .decision import Decision, Reason
47
+ from .errors import (
48
+ AlreadyClosedError,
49
+ AuthenticationError,
50
+ AuthorizationError,
51
+ BackendUnavailableError,
52
+ ConfigurationError,
53
+ ErrorKind,
54
+ FireweaveError,
55
+ FlagNotFoundError,
56
+ InternalError,
57
+ InvalidContextError,
58
+ MalformedResponseError,
59
+ NetworkError,
60
+ NotReadyError,
61
+ RateLimitedError,
62
+ TargetingKeyMissingError,
63
+ TimeoutError_,
64
+ TypeMismatchError,
65
+ UnsupportedCapabilityError,
66
+ )
67
+ from .runtime import EvaluationOptions, FireweaveRuntime, LifecycleState
68
+ from .types import FlagType, JsonValue
69
+
70
+ __all__ = [
71
+ "__version__",
72
+ "SPEC_VERSION",
73
+ # runtime / client
74
+ "FireweaveClient",
75
+ "FireweaveRuntime",
76
+ "FireweaveConfig",
77
+ "DEFAULT_ALLOWED_HOSTS",
78
+ "LifecycleState",
79
+ "EvaluationOptions",
80
+ # adapters
81
+ "BackendAdapter",
82
+ "FlagResolution",
83
+ "InMemoryAdapter",
84
+ "FireweaveLocalAdapter",
85
+ "FireweaveRemoteAdapter",
86
+ "RegisterTargetOptions",
87
+ "RegisterTargetResult",
88
+ # context / decisions
89
+ "ContextLimits",
90
+ "EvaluationContext",
91
+ "merge_contexts",
92
+ "validate_context",
93
+ "Decision",
94
+ "Reason",
95
+ "FlagType",
96
+ "JsonValue",
97
+ # capabilities
98
+ "CANONICAL_CAPABILITIES",
99
+ "CapabilityRegistry",
100
+ # extension results
101
+ "ReleaseContext",
102
+ "ReleaseResult",
103
+ "ExposureResult",
104
+ "FlushResult",
105
+ "SignalResult",
106
+ "CapabilityResult",
107
+ # errors
108
+ "ErrorKind",
109
+ "FireweaveError",
110
+ "NotReadyError",
111
+ "FlagNotFoundError",
112
+ "TypeMismatchError",
113
+ "InvalidContextError",
114
+ "TargetingKeyMissingError",
115
+ "AuthenticationError",
116
+ "AuthorizationError",
117
+ "RateLimitedError",
118
+ "TimeoutError_",
119
+ "NetworkError",
120
+ "BackendUnavailableError",
121
+ "MalformedResponseError",
122
+ "UnsupportedCapabilityError",
123
+ "ConfigurationError",
124
+ "AlreadyClosedError",
125
+ "InternalError",
126
+ ]
@@ -0,0 +1,5 @@
1
+ """Package version constants."""
2
+
3
+ __version__ = "0.1.0"
4
+ SPEC_VERSION = "0.1.0"
5
+ OPENFEATURE_SPEC_FLOOR = "0.8.0"
@@ -0,0 +1,23 @@
1
+ """Backend adapters: protocol, in-memory, local, remote (default), PostHog (optional)."""
2
+
3
+ from .base import (
4
+ BackendAdapter,
5
+ FlagResolution,
6
+ RegisterTargetOptions,
7
+ RegisterTargetResult,
8
+ TargetKind,
9
+ )
10
+ from .local import FireweaveLocalAdapter
11
+ from .memory import InMemoryAdapter
12
+ from .remote import FireweaveRemoteAdapter
13
+
14
+ __all__ = [
15
+ "BackendAdapter",
16
+ "FlagResolution",
17
+ "RegisterTargetOptions",
18
+ "RegisterTargetResult",
19
+ "TargetKind",
20
+ "InMemoryAdapter",
21
+ "FireweaveLocalAdapter",
22
+ "FireweaveRemoteAdapter",
23
+ ]
@@ -0,0 +1,117 @@
1
+ """BackendAdapter protocol and vendor-neutral resolution record.
2
+
3
+ Adapters translate a vendor backend (PostHog, in-memory fixtures, ...) into
4
+ :class:`FlagResolution` records. No vendor types cross this boundary — the
5
+ runtime and public API only ever see Fireweave-owned shapes.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass, field
11
+ from typing import Any, Dict, Literal, Optional, Protocol, runtime_checkable
12
+
13
+ from ..context import EvaluationContext
14
+ from ..errors import FireweaveError
15
+ from ..types import JsonValue
16
+
17
+ __all__ = [
18
+ "BackendAdapter",
19
+ "FlagResolution",
20
+ "TargetKind",
21
+ "RegisterTargetOptions",
22
+ "RegisterTargetResult",
23
+ ]
24
+
25
+ TargetKind = Literal["user", "device"]
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class RegisterTargetOptions:
30
+ """Options for ``POST /v1/targets/register`` (spec/remote-register-target).
31
+
32
+ Omitted fields are left off the wire rather than sent as null/default —
33
+ the server defaults ``kind`` to ``user`` when absent.
34
+ """
35
+
36
+ kind: Optional[TargetKind] = None
37
+ properties: Optional[Dict[str, JsonValue]] = None
38
+ environment: Optional[str] = None
39
+
40
+
41
+ @dataclass(frozen=True)
42
+ class RegisterTargetResult:
43
+ """Outcome of target registration.
44
+
45
+ ``ok=False`` means the target was NOT registered — rules that depend on its
46
+ properties will not match until a later attempt succeeds. Callers in a
47
+ login path normally ignore this; a careful caller logs it.
48
+ """
49
+
50
+ ok: bool
51
+ error: Optional[FireweaveError] = None
52
+
53
+
54
+ @dataclass(frozen=True)
55
+ class FlagResolution:
56
+ """Vendor-neutral outcome of resolving one flag.
57
+
58
+ ``matched`` is False when targeting conditions did not select the caller
59
+ (the runtime then serves the caller default with reason DEFAULT).
60
+ ``fireweave_reason`` lets adapters force a canonical reason (e.g. SPLIT).
61
+ """
62
+
63
+ value: JsonValue
64
+ variant: Optional[str] = None
65
+ enabled: bool = True
66
+ matched: bool = True
67
+ version: Optional[int] = None
68
+ vendor_flag_id: Optional[int] = None
69
+ reason_code: Optional[str] = None
70
+ condition_index: Optional[int] = None
71
+ payload: Optional[JsonValue] = None
72
+ fireweave_reason: Optional[str] = None
73
+ from_cache: bool = False
74
+ quota_limited: bool = False
75
+ extra_metadata: Dict[str, Any] = field(default_factory=dict)
76
+
77
+
78
+ @runtime_checkable
79
+ class BackendAdapter(Protocol):
80
+ """Protocol every Fireweave backend adapter implements.
81
+
82
+ ``resolve`` raises a :class:`fireweave.errors.FireweaveError` subtype on
83
+ failure (FlagNotFound, Network, Timeout, ...); the runtime converts errors
84
+ into default-valued decisions — evaluation APIs never propagate them.
85
+ """
86
+
87
+ def initialize(self) -> None:
88
+ """Bring the backend up; raise FireweaveError on fatal config."""
89
+
90
+ def resolve(self, flag_key: str, context: EvaluationContext) -> FlagResolution:
91
+ """Resolve one flag against a validated, merged context."""
92
+
93
+ def shutdown(self, timeout_ms: int) -> None:
94
+ """Deterministically flush and release resources within ``timeout_ms``.
95
+
96
+ Idempotent; must never block past the deadline.
97
+ """
98
+
99
+
100
+ # Optional adapter surface (duck-typed, ruling 17 / capabilities matrix):
101
+ #
102
+ # - ``backend_name: str`` — "fireweave" | "posthog" | "inmemory" | "none" | "other"
103
+ # (spec/capabilities.schema.json runtime.backend).
104
+ # - ``runtime_features() -> Dict[str, bool]`` — adapter-dependent runtime
105
+ # capability booleans merged into ``capabilities.get()``.
106
+ # - ``register_target(targeting_key, options) -> RegisterTargetResult`` —
107
+ # durable identity registration (``POST /v1/targets/register``). Returns
108
+ # ``ok=False`` rather than raising — registration sits in login paths.
109
+ # - ``send_exposures(events: list) -> None`` — telemetry sink for flushed
110
+ # exposure events.
111
+ # - ``deliver_signal(signal: Dict) -> None`` — telemetry sink for recorded
112
+ # signals.
113
+ # - ``deliver_release(event: Dict) -> None`` — telemetry sink for release
114
+ # transitions.
115
+ #
116
+ # All sink methods are best-effort: implementations swallow vendor errors —
117
+ # telemetry loss must never affect callers.
@@ -0,0 +1,76 @@
1
+ """Local development adapter — the DEV substrate for a scaffolded harness.
2
+
3
+ Counterpart to :class:`FireweaveRemoteAdapter`: prod evaluates control points
4
+ against fw-server; dev evaluates them here, in-process, with no network and no
5
+ credentials. Because it satisfies the same :class:`BackendAdapter` port, the
6
+ dev branch of a harness runs through the same :class:`FireweaveRuntime` as
7
+ prod — inheriting identical lifecycle gating and context canonicalization.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Dict, Mapping, Optional
13
+
14
+ from ..context import EvaluationContext
15
+ from ..errors import FlagNotFoundError
16
+ from .base import FlagResolution
17
+
18
+ __all__ = ["FireweaveLocalAdapter"]
19
+
20
+
21
+ class FireweaveLocalAdapter:
22
+ """In-process boolean overrides for local development.
23
+
24
+ Resolution policy:
25
+
26
+ - a key present in ``dev_flags`` resolves to its mapped value with reason
27
+ ``STATIC``;
28
+ - every other key raises :class:`FlagNotFoundError`, which the runtime
29
+ turns into an ERROR decision. :func:`make_fireweave_local_provider`
30
+ rewrites that single outcome to a clean DEFAULT for the OpenFeature path.
31
+ """
32
+
33
+ backend_name = "other"
34
+
35
+ def __init__(self, dev_flags: Optional[Mapping[str, bool]] = None) -> None:
36
+ self._dev_flags: Dict[str, bool] = dict(dev_flags or {})
37
+ self._closed = False
38
+
39
+ def initialize(self) -> None:
40
+ self._closed = False
41
+
42
+ def resolve(self, flag_key: str, context: EvaluationContext) -> FlagResolution:
43
+ # A ``dev_flags`` hit reports ``enabled=True`` alongside reason STATIC.
44
+ # Reporting ``enabled=False`` for an override of ``False`` would make
45
+ # the runtime label the decision DISABLED — "switched off upstream" —
46
+ # not what a local override expresses.
47
+ #
48
+ # Values are always boolean, so reading an overridden key as a string
49
+ # or number yields TYPE_MISMATCH rather than silently defaulting.
50
+ del context # unused; kept for BackendAdapter signature parity
51
+ if flag_key not in self._dev_flags:
52
+ raise FlagNotFoundError()
53
+ override = self._dev_flags[flag_key]
54
+ return FlagResolution(
55
+ value=override,
56
+ variant="on" if override else "off",
57
+ enabled=True,
58
+ matched=True,
59
+ fireweave_reason="STATIC",
60
+ )
61
+
62
+ def shutdown(self, timeout_ms: int) -> None:
63
+ del timeout_ms
64
+ self._closed = True
65
+
66
+ def runtime_features(self) -> Dict[str, bool]:
67
+ return {
68
+ "remoteEvaluation": False,
69
+ "localEvaluation": True,
70
+ "localOnly": True,
71
+ # No exposure sink exists locally; claiming otherwise would make
72
+ # capabilities.get() advertise emission that silently goes nowhere.
73
+ "exposureEmission": False,
74
+ "sideEffectFreeReads": True,
75
+ "groupAnalytics": False,
76
+ }