graphrefly 0.1.0__py3-none-any.whl

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 (47) hide show
  1. graphrefly/__init__.py +160 -0
  2. graphrefly/compat/__init__.py +18 -0
  3. graphrefly/compat/async_utils.py +228 -0
  4. graphrefly/compat/asyncio_runner.py +89 -0
  5. graphrefly/compat/trio_runner.py +81 -0
  6. graphrefly/core/__init__.py +142 -0
  7. graphrefly/core/clock.py +20 -0
  8. graphrefly/core/dynamic_node.py +749 -0
  9. graphrefly/core/guard.py +277 -0
  10. graphrefly/core/meta.py +149 -0
  11. graphrefly/core/node.py +963 -0
  12. graphrefly/core/protocol.py +460 -0
  13. graphrefly/core/runner.py +107 -0
  14. graphrefly/core/subgraph_locks.py +296 -0
  15. graphrefly/core/sugar.py +138 -0
  16. graphrefly/core/versioning.py +193 -0
  17. graphrefly/extra/__init__.py +313 -0
  18. graphrefly/extra/adapters.py +2149 -0
  19. graphrefly/extra/backoff.py +287 -0
  20. graphrefly/extra/backpressure.py +113 -0
  21. graphrefly/extra/checkpoint.py +307 -0
  22. graphrefly/extra/composite.py +303 -0
  23. graphrefly/extra/cron.py +133 -0
  24. graphrefly/extra/data_structures.py +707 -0
  25. graphrefly/extra/resilience.py +727 -0
  26. graphrefly/extra/sources.py +766 -0
  27. graphrefly/extra/tier1.py +1067 -0
  28. graphrefly/extra/tier2.py +1802 -0
  29. graphrefly/graph/__init__.py +31 -0
  30. graphrefly/graph/graph.py +2249 -0
  31. graphrefly/integrations/__init__.py +1 -0
  32. graphrefly/integrations/fastapi.py +767 -0
  33. graphrefly/patterns/__init__.py +5 -0
  34. graphrefly/patterns/ai.py +2132 -0
  35. graphrefly/patterns/cqrs.py +515 -0
  36. graphrefly/patterns/memory.py +639 -0
  37. graphrefly/patterns/messaging.py +553 -0
  38. graphrefly/patterns/orchestration.py +536 -0
  39. graphrefly/patterns/reactive_layout/__init__.py +81 -0
  40. graphrefly/patterns/reactive_layout/measurement_adapters.py +276 -0
  41. graphrefly/patterns/reactive_layout/reactive_block_layout.py +434 -0
  42. graphrefly/patterns/reactive_layout/reactive_layout.py +943 -0
  43. graphrefly/py.typed +1 -0
  44. graphrefly-0.1.0.dist-info/METADATA +253 -0
  45. graphrefly-0.1.0.dist-info/RECORD +47 -0
  46. graphrefly-0.1.0.dist-info/WHEEL +4 -0
  47. graphrefly-0.1.0.dist-info/licenses/LICENSE +21 -0
graphrefly/py.typed ADDED
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,253 @@
1
+ Metadata-Version: 2.4
2
+ Name: graphrefly
3
+ Version: 0.1.0
4
+ Summary: Reactive graph protocol for human + LLM co-operation. Composable nodes, glitch-free diamond resolution, two-phase push, durable streaming. Zero dependencies.
5
+ Project-URL: Homepage, https://py.graphrefly.dev
6
+ Project-URL: Repository, https://github.com/graphrefly/graphrefly-py
7
+ Project-URL: Documentation, https://py.graphrefly.dev
8
+ Author: David Chen
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai-agents,asyncio,callbag,diamond-resolution,durable-workflow,graph,llm,observable,orchestration,reactive,reactive-programming,signals,state-management,streaming,zero-dependency
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Framework :: AsyncIO
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.12
24
+ Description-Content-Type: text/markdown
25
+
26
+ # GraphReFly
27
+
28
+ **Reactive graph protocol for human + LLM co-operation.**
29
+
30
+ One primitive. Zero dependencies. Composable nodes with glitch-free diamond resolution, two-phase push propagation, durable streaming, and async runners for asyncio and trio.
31
+
32
+ [![PyPI](https://img.shields.io/pypi/v/graphrefly-py?color=blue)](https://pypi.org/project/graphrefly-py/)
33
+ [![license](https://img.shields.io/github/license/graphrefly/graphrefly-py)](./LICENSE)
34
+ [![Python](https://img.shields.io/pypi/pyversions/graphrefly-py)](https://pypi.org/project/graphrefly-py/)
35
+
36
+ [Docs](https://py.graphrefly.dev) | [Spec](https://py.graphrefly.dev/spec/) | [TypeScript](https://graphrefly.dev) | [API Reference](https://py.graphrefly.dev/api/)
37
+
38
+ ---
39
+
40
+ ## Quick start
41
+
42
+ ```bash
43
+ pip install graphrefly-py
44
+ ```
45
+
46
+ ```python
47
+ from graphrefly import state, derived, effect
48
+
49
+ count = state(0)
50
+ doubled = derived([count], lambda deps, _: deps[0] * 2)
51
+
52
+ effect([doubled], lambda deps, _: print("doubled:", deps[0]))
53
+ # → doubled: 0
54
+
55
+ count.push(3)
56
+ # → doubled: 6
57
+ ```
58
+
59
+ ## Why GraphReFly?
60
+
61
+ Most state libraries solve **one** problem well. GraphReFly solves the space between them:
62
+
63
+ | | Redux / Zustand | RxPY | Pydantic AI | TC39 Signals | **GraphReFly** |
64
+ |--|-----------------|------|-------------|-------------|---------------|
65
+ | Simple store API | yes | no | no | yes | **yes** |
66
+ | Streaming operators | no | yes | no | no | **yes** |
67
+ | Diamond resolution | no | n/a | n/a | partial | **glitch-free** |
68
+ | Graph introspection | no | no | no | no | **describe / observe / diagram** |
69
+ | Durable checkpoints | no | no | no | no | **file / SQLite / IndexedDB** |
70
+ | LLM orchestration | no | no | partial | no | **agent_loop / chat_stream / tool_registry** |
71
+ | Async runners | n/a | asyncio | asyncio | n/a | **asyncio / trio** |
72
+ | Dependencies | varies | 0 | many | n/a | **0** |
73
+
74
+ ## One primitive
75
+
76
+ Everything is a `node`. Sugar constructors give you the right shape:
77
+
78
+ ```python
79
+ from graphrefly import state, derived, producer, effect
80
+ from graphrefly.core.messages import DATA
81
+
82
+ # Writable state
83
+ name = state("world")
84
+
85
+ # Computed (re-runs when deps change)
86
+ greeting = derived([name], lambda deps, _: f"Hello, {deps[0]}!")
87
+
88
+ # Push source (timers, events, async streams)
89
+ clock = producer(lambda emit, _: emit([(DATA, time.time())]))
90
+
91
+ # Side effect
92
+ effect([greeting], lambda deps, _: print(deps[0]))
93
+ ```
94
+
95
+ ## Streaming & operators
96
+
97
+ 70+ operators — transform, combine, buffer, window, rate-limit, retry, circuit-break:
98
+
99
+ ```python
100
+ from graphrefly.extra.tier1 import map_op, filter_op, scan
101
+ from graphrefly.extra.tier2 import switch_map, debounce_time
102
+ from graphrefly.extra.resilience import retry
103
+ from graphrefly import pipe
104
+
105
+ search = pipe(
106
+ user_input,
107
+ debounce_time(0.3),
108
+ switch_map(lambda q: from_promise(fetch(f"/api?q={q}"))),
109
+ retry(strategy="exponential", max_attempts=3),
110
+ )
111
+ ```
112
+
113
+ ## Graph container
114
+
115
+ Register nodes in a `Graph` for introspection, snapshot, and persistence:
116
+
117
+ ```python
118
+ from graphrefly import Graph, state, derived
119
+
120
+ g = Graph("pricing")
121
+ price = g.register("price", state(100))
122
+ tax = g.register("tax", derived([price], lambda d, _: d[0] * 0.1))
123
+ total = g.register("total", derived([price, tax], lambda d, _: d[0] + d[1]))
124
+
125
+ g.describe() # → full graph topology as dict
126
+ g.diagram() # → Mermaid diagram string
127
+ g.observe(lambda e: print(e)) # → live change stream
128
+ ```
129
+
130
+ ## AI & orchestration
131
+
132
+ First-class patterns for LLM streaming, agent loops, and human-in-the-loop workflows:
133
+
134
+ ```python
135
+ from graphrefly.patterns.ai import chat_stream, agent_loop, tool_registry
136
+ from graphrefly.patterns.memory import collection, decay
137
+
138
+ # Streaming chat with tool use
139
+ chat = chat_stream("assistant", model="claude-sonnet-4-20250514",
140
+ tools=tool_registry("tools", search=search_fn))
141
+
142
+ # Full agent loop: observe → think → act → memory
143
+ agent = agent_loop("researcher", llm=chat,
144
+ memory=agent_memory(decay="openviking"))
145
+ ```
146
+
147
+ ## Async runners
148
+
149
+ Native asyncio and trio support for async sources and long-running graphs:
150
+
151
+ ```python
152
+ from graphrefly.compat.asyncio_runner import AsyncioRunner
153
+ from graphrefly.extra.sources import from_async_iter
154
+
155
+ # Wrap an async generator as a reactive node
156
+ async def sse_events():
157
+ async for event in httpx_client.stream("GET", "/events"):
158
+ yield event.data
159
+
160
+ events = from_async_iter(sse_events())
161
+
162
+ # Run the graph in an asyncio event loop
163
+ runner = AsyncioRunner(graph)
164
+ await runner.run()
165
+ ```
166
+
167
+ ## FastAPI integration
168
+
169
+ Drop-in integration for reactive backends:
170
+
171
+ ```python
172
+ from graphrefly.integrations.fastapi import GraphReflyRouter
173
+
174
+ router = GraphReflyRouter(graph)
175
+ app.include_router(router, prefix="/graph")
176
+ # GET /graph/describe → graph topology
177
+ # GET /graph/snapshot → current state
178
+ # WS /graph/observe → live change stream
179
+ ```
180
+
181
+ ## Resilience & checkpoints
182
+
183
+ Built-in retry, circuit breakers, rate limiters, and persistent checkpoints:
184
+
185
+ ```python
186
+ from graphrefly.extra.resilience import retry, circuit_breaker, rate_limiter
187
+ from graphrefly.extra.checkpoint import FileCheckpointAdapter, save_graph_checkpoint
188
+
189
+ # Retry with exponential backoff
190
+ resilient = pipe(source, retry(strategy="exponential"))
191
+
192
+ # Circuit breaker
193
+ breaker = circuit_breaker(threshold=5, reset_timeout=30.0)
194
+
195
+ # Checkpoint to file system
196
+ adapter = FileCheckpointAdapter("./checkpoints")
197
+ save_graph_checkpoint(graph, adapter)
198
+ ```
199
+
200
+ ## Project layout
201
+
202
+ | Path | Contents |
203
+ |------|----------|
204
+ | `src/graphrefly/core/` | Message protocol, `node` primitive, batch, sugar constructors |
205
+ | `src/graphrefly/extra/` | Operators, sources, data structures, resilience, checkpoints |
206
+ | `src/graphrefly/graph/` | `Graph` container, describe/observe, snapshot, persistence |
207
+ | `src/graphrefly/patterns/` | Orchestration, messaging, memory, AI, CQRS, reactive layout |
208
+ | `src/graphrefly/compat/` | Async runners (asyncio, trio) |
209
+ | `src/graphrefly/integrations/` | Framework integrations (FastAPI) |
210
+ | `docs/` | Roadmap, guidance, benchmarks |
211
+ | `website/` | Astro + Starlight docs site ([py.graphrefly.dev](https://py.graphrefly.dev)) |
212
+
213
+ ## Scripts
214
+
215
+ ```bash
216
+ uv run pytest # run tests
217
+ uv run ruff check . # lint
218
+ uv run mypy src/ # type check
219
+ uv run pytest --benchmark # benchmarks
220
+ ```
221
+
222
+ ## Requirements
223
+
224
+ Python 3.12 or later. Zero runtime dependencies.
225
+
226
+ ## Acknowledgments
227
+
228
+ GraphReFly builds on ideas from many projects and papers:
229
+
230
+ **Protocol & predecessor:**
231
+ - **[Callbag](https://github.com/callbag/callbag)** (Andre Staltz) — the original reactive protocol spec. GraphReFly's message-based node communication descends from callbag's function-calling-function model.
232
+ - **[callbag-recharge](https://github.com/Callbag-Recharge/callbag-recharge)** & **[callbag-recharge-py](https://github.com/Callbag-Recharge/callbag-recharge-py)** — GraphReFly's direct predecessors. The Python port (6 primitives, 18 operators, 100+ tests) established cross-language parity patterns carried forward.
233
+
234
+ **Reactive design patterns:**
235
+ - **[SolidJS](https://github.com/solidjs/solid)** — two-phase execution (DIRTY propagation + value flow), automatic caching, and effect batching. Closest philosophical neighbor.
236
+ - **[Preact Signals](https://github.com/preactjs/signals)** — fine-grained reactivity and cached-flag optimization patterns that informed RESOLVED signal design.
237
+ - **[TC39 Signals Proposal](https://github.com/tc39/proposal-signals)** — the `.get()/.set()` contract and the push toward language-level reactivity.
238
+ - **[RxJS](https://github.com/ReactiveX/rxjs)** / **[RxPY](https://github.com/ReactiveX/RxPY)** — operator naming conventions and the DevTools observability philosophy that inspired the Inspector pattern.
239
+
240
+ **AI & memory:**
241
+ - **[OpenViking](https://github.com/volcengine/openviking)** (Volcengine) — the memory decay formula (`sigmoid(log1p(count)) * exp_decay(age, 7d)`) and L0/L1/L2 progressive loading strategy used in `agent_memory()`.
242
+ - **[FadeMem](https://arxiv.org/abs/2501.09399)** (Wei et al., ICASSP 2026) — biologically-inspired dual-layer memory with adaptive exponential decay.
243
+ - **[MAGMA](https://arxiv.org/abs/2501.13920)** (Jiang et al., 2026) — four-parallel-graph model (semantic/temporal/causal/entity) that informed `knowledge_graph()` design.
244
+ - **[Letta/MemGPT](https://github.com/letta-ai/letta)**, **[Mem0](https://github.com/mem0ai/mem0)**, **[Zep/Graphiti](https://github.com/getzep/graphiti)**, **[Cognee](https://github.com/topoteretes/cognee)** — production memory architectures surveyed during `agent_memory()` design.
245
+
246
+ **Layout & other:**
247
+ - **[Pretext](https://github.com/chenglou/pretext)** (Cheng Lou) — inspired the reactive layout engine's DOM-free text measurement pipeline.
248
+ - **[CASL](https://github.com/stalniy/casl)** — declarative `allow()`/`deny()` policy builder DX that inspired `policy()`.
249
+ - **[Nanostores](https://github.com/nanostores/nanostores)** — tiny framework-agnostic API with `.get()/.set()/.subscribe()` mapping that validated the store ergonomics.
250
+
251
+ ## License
252
+
253
+ [MIT](./LICENSE)
@@ -0,0 +1,47 @@
1
+ graphrefly/__init__.py,sha256=wXd13rcP39eu4yrqZ4ycCWDyzmiLtXUpMQgdwPzvluU,3040
2
+ graphrefly/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
3
+ graphrefly/compat/__init__.py,sha256=HWfCAXg5kKuo7s8qy6r-nSElyWQnQ24MDpI8YmTgZ2g,496
4
+ graphrefly/compat/async_utils.py,sha256=1pCwdJwU4AJwiMScrgNNM3VEuEiBvEKOWyKHPJQJo9s,7501
5
+ graphrefly/compat/asyncio_runner.py,sha256=fDFh1ej6n4ziaRbzjQrU__DsG8x2zOFxc46cDAUg74c,2460
6
+ graphrefly/compat/trio_runner.py,sha256=Z8RT-JzMedzJuGgurzb1PSitpuOPP2UeBmfWmoSUGJA,2079
7
+ graphrefly/core/__init__.py,sha256=UdX9w8bVZf07sIxPGByN20lf6FHtbYHf0buQqk2gTWU,2820
8
+ graphrefly/core/clock.py,sha256=AxtKX8yj5a5NG-JoUo5jrXemWuOAckjQ2HlbkiqzhGA,611
9
+ graphrefly/core/dynamic_node.py,sha256=zHUezZv2WwKrnXTdnifYzqREd_jx0gkvvEiPtX7lmP0,25589
10
+ graphrefly/core/guard.py,sha256=snNOkmf2fDvOorjMWYuEYKEWrVSgLzgMNqnuTKJ_Dds,8879
11
+ graphrefly/core/meta.py,sha256=3IqCx6zZ6d2aYkaUeY8POvh8o6btXFDhZP43N9v_1vY,4970
12
+ graphrefly/core/node.py,sha256=XN9wtRkJOdzzfKHErRhFyI_GDccBTuTkqtuxDd43sbU,33436
13
+ graphrefly/core/protocol.py,sha256=DNNeiGXW3DcOz1k8T030ZjYrK1R67OAMMNFG8_rk6yM,16121
14
+ graphrefly/core/runner.py,sha256=R-1jpJ9wsVbCGfQd6oNgzBpF_HatT1pAtlmdTbxMcBg,3281
15
+ graphrefly/core/subgraph_locks.py,sha256=xa4563Pd_67gd0AVdRbsMlVAJXNq_9tMGqwicQTC2A8,10348
16
+ graphrefly/core/sugar.py,sha256=9FW9BWrose6CHP-mfEd10z6EacJQpNNYMVCoFu5Yx5k,4276
17
+ graphrefly/core/versioning.py,sha256=mHBDN6PUoRj_IkpAWW1tIdmeTo9bdP33u1Ak4GzmLNw,6177
18
+ graphrefly/extra/__init__.py,sha256=vjuK5HXg5sDzAs4woV9IAfliT0H4cR45iVGlY-QG6As,5714
19
+ graphrefly/extra/adapters.py,sha256=J1ItOwl92Cw7XGWpfpjKbXc1Tqga8BXdBiIGHstlAnQ,71844
20
+ graphrefly/extra/backoff.py,sha256=oNPrspj1lsKAd7X_nvlPL1hm35EewcLQgImHkdw-UBY,8682
21
+ graphrefly/extra/backpressure.py,sha256=BuwC05lxgdD5AXTN3Btk8PbpCUp70va05B8PM2GvKeo,3614
22
+ graphrefly/extra/checkpoint.py,sha256=2REZmSS4u5lwsS-8WW6T26WjpWsokJQWxOw87oWYfSg,10213
23
+ graphrefly/extra/composite.py,sha256=75ExK2tBkHIIVjD7EwzTZKnhqvXQQr0TYguWVXOpsGw,9894
24
+ graphrefly/extra/cron.py,sha256=tvFSz6l_H2JED5YeRmn1Fdo_jtY9eDJInCfOfAVci_I,4341
25
+ graphrefly/extra/data_structures.py,sha256=YIxYKbcNa3w-wbmgBdZQmSlVK1mCUaTZvFqsueDBWPs,22759
26
+ graphrefly/extra/resilience.py,sha256=n4pzyC89qrZ8MwOZl8lhQdIsIN4w9rljCo-TfRtykEA,24283
27
+ graphrefly/extra/sources.py,sha256=KswMDju2gp8mt3tfob3OgGnrMgy3S7b9ewdjcXJ7aHM,23194
28
+ graphrefly/extra/tier1.py,sha256=vIF1w0iqEJTCuwkbaDpwt18NXHzX8wpCrIHuPFlQhQ0,32452
29
+ graphrefly/extra/tier2.py,sha256=8_pwI5r_0vfQZWHiQd-JdFB-8NOXloVRqOsYrkzbrbA,58319
30
+ graphrefly/graph/__init__.py,sha256=q7KGCueLgmc8fc--yHuBbOcmcE8J621We-EwPhziB5Y,624
31
+ graphrefly/graph/graph.py,sha256=J1eBNeXLErljPNQCVw5_0v1rTHTC0xUWuh2lUQ8rzkg,85114
32
+ graphrefly/integrations/__init__.py,sha256=TQIsQDM4E8lUR52P-qQx6XTC6Rbtl_hhsSdFGAD0rhc,61
33
+ graphrefly/integrations/fastapi.py,sha256=1TCH0k2Wi7OuZaNoY1LggU7p0KGCl0GYAK3DjfZXh4U,25795
34
+ graphrefly/patterns/__init__.py,sha256=Lyxovgdt9OCtqde7D1hw2YMzCWH1z1CfKXFCmffT_e0,226
35
+ graphrefly/patterns/ai.py,sha256=z6ko-vvZTmajxu--3VorlI_qPckrhfpSCWR9m4AqC54,73538
36
+ graphrefly/patterns/cqrs.py,sha256=2doCBqwe78qV7epP9uTtpgblWawuuBsjqKYcvwpZzt8,17716
37
+ graphrefly/patterns/memory.py,sha256=NlSjp1Lu7UdLeVW59R0EdIEAJAZqWjmVp30q49ZqaW8,20422
38
+ graphrefly/patterns/messaging.py,sha256=ejGgcrM8DW0e1FphVHNOhD6Nnsn0LpA3yHIzNtImFGQ,17891
39
+ graphrefly/patterns/orchestration.py,sha256=Zs60tzL5yCHT--kSAj_tNCcbaVhivIiaI5ZIqDA6cIo,15155
40
+ graphrefly/patterns/reactive_layout/__init__.py,sha256=6jn8hzU9cLn1tbE7Ot06DPtAMIQouXhqIU8RAYQ8qbo,1872
41
+ graphrefly/patterns/reactive_layout/measurement_adapters.py,sha256=ob8k1wzVylTd2HOBFSryD7O8lsPRZnpUju9O3jRziSs,9485
42
+ graphrefly/patterns/reactive_layout/reactive_block_layout.py,sha256=l8gn2nJwQMm_pEz8dVDt8fUrStZJmLc-gvdvFoqCb2g,14356
43
+ graphrefly/patterns/reactive_layout/reactive_layout.py,sha256=-pqO2Cu-1MQsecxDHbb3VKJ8tj6flMzUPR0NZoIH8NA,32385
44
+ graphrefly-0.1.0.dist-info/METADATA,sha256=PXbBAKwq5SHH8UHp3sEDdMmB5q5b93II_cHGif1SnS8,10278
45
+ graphrefly-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
46
+ graphrefly-0.1.0.dist-info/licenses/LICENSE,sha256=gcKfYEH-2_EdipDV2fzR2hW6Sp15JX3lSyg7eS47x0U,1067
47
+ graphrefly-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 David Chen
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.