cairns 0.2.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.
- cairns-0.2.0/.envrc +1 -0
- cairns-0.2.0/.gitignore +6 -0
- cairns-0.2.0/.import_linter_cache/.gitignore +2 -0
- cairns-0.2.0/.import_linter_cache/6ff22d18cc797dcfbda9e438db40df94c9091ca4.data.json +1 -0
- cairns-0.2.0/.import_linter_cache/CACHEDIR.TAG +3 -0
- cairns-0.2.0/.import_linter_cache/cairn.meta.json +1 -0
- cairns-0.2.0/.import_linter_cache/cairns.meta.json +1 -0
- cairns-0.2.0/.import_linter_cache/ea33efcc55ca9faff44446ae7f31abced923d943.data.json +1 -0
- cairns-0.2.0/LICENSE +21 -0
- cairns-0.2.0/PKG-INFO +178 -0
- cairns-0.2.0/README.md +136 -0
- cairns-0.2.0/Taskfile.yaml +34 -0
- cairns-0.2.0/devenv.lock +103 -0
- cairns-0.2.0/devenv.nix +21 -0
- cairns-0.2.0/devenv.yaml +6 -0
- cairns-0.2.0/docs/design.md +453 -0
- cairns-0.2.0/docs/logo.jpg +0 -0
- cairns-0.2.0/docs/motivation.md +80 -0
- cairns-0.2.0/docs/patterns.md +529 -0
- cairns-0.2.0/docs/store-design.md +617 -0
- cairns-0.2.0/docs/todo/anyio.md +34 -0
- cairns-0.2.0/docs/todo/nominal-identity.md +174 -0
- cairns-0.2.0/examples/failure_resume.py +90 -0
- cairns-0.2.0/examples/hitl.py +65 -0
- cairns-0.2.0/examples/research_fake_llm.py +182 -0
- cairns-0.2.0/examples/research_haiku.py +143 -0
- cairns-0.2.0/examples/rlhf_mock.py +260 -0
- cairns-0.2.0/examples/scraper.py +110 -0
- cairns-0.2.0/pyproject.toml +100 -0
- cairns-0.2.0/src/cairns/__init__.py +50 -0
- cairns-0.2.0/src/cairns/cli/__init__.py +219 -0
- cairns-0.2.0/src/cairns/core/__init__.py +91 -0
- cairns-0.2.0/src/cairns/core/cairn.py +98 -0
- cairns-0.2.0/src/cairns/core/hash.py +178 -0
- cairns-0.2.0/src/cairns/core/lock.py +65 -0
- cairns-0.2.0/src/cairns/core/runtime.py +376 -0
- cairns-0.2.0/src/cairns/core/serial.py +144 -0
- cairns-0.2.0/src/cairns/core/sink.py +80 -0
- cairns-0.2.0/src/cairns/core/step.py +720 -0
- cairns-0.2.0/src/cairns/core/store.py +621 -0
- cairns-0.2.0/src/cairns/core/types.py +340 -0
- cairns-0.2.0/src/cairns/ext/__init__.py +0 -0
- cairns-0.2.0/src/cairns/ext/claude.py +244 -0
- cairns-0.2.0/src/cairns/interaction/__init__.py +250 -0
- cairns-0.2.0/src/cairns/patterns/__init__.py +58 -0
- cairns-0.2.0/src/cairns/patterns/semver.py +55 -0
- cairns-0.2.0/src/cairns/py.typed +0 -0
- cairns-0.2.0/src/cairns/run/__init__.py +271 -0
- cairns-0.2.0/src/cairns/run/gc.py +266 -0
- cairns-0.2.0/src/cairns/run/show.py +306 -0
- cairns-0.2.0/src/cairns/run/spans.py +183 -0
- cairns-0.2.0/src/cairns/testing.py +144 -0
- cairns-0.2.0/src/cairns/tui/__init__.py +31 -0
- cairns-0.2.0/src/cairns/tui/app.py +738 -0
- cairns-0.2.0/src/cairns/tui/messages.py +91 -0
- cairns-0.2.0/src/cairns/tui/render.py +43 -0
- cairns-0.2.0/src/cairns/tui/sinks.py +110 -0
- cairns-0.2.0/src/cairns/tui/widgets.py +144 -0
- cairns-0.2.0/tests/conftest.py +1 -0
- cairns-0.2.0/tests/test_core.py +696 -0
- cairns-0.2.0/tests/test_disk.py +432 -0
- cairns-0.2.0/tests/test_gc.py +241 -0
- cairns-0.2.0/tests/test_hash.py +522 -0
- cairns-0.2.0/tests/test_metrics.py +291 -0
- cairns-0.2.0/tests/test_patterns.py +276 -0
- cairns-0.2.0/tests/test_resume.py +143 -0
- cairns-0.2.0/tests/test_serial.py +120 -0
- cairns-0.2.0/uv.lock +444 -0
cairns-0.2.0/.envrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
use devenv
|
cairns-0.2.0/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"cairn.run.spans":[],"cairn.run.show":[["cairn.core",174,"from cairn.core import event_to_dict"],["cairn.run.gc",17,"from .gc import list_runs"],["cairn.core",16,"from cairn.core import Event"],["cairn.run.spans",18,"from .spans import SpanGraph"]],"cairn.tui.messages":[],"cairn.tui.app":[["cairn.core",21,"from cairn.core import CompositeSink, Handle, set_sink, set_store"],["cairn.tui.sinks",34,"from .sinks import TuiInteractionSink, TuiSink"],["cairn.interaction",22,"from cairn.interaction import set_interaction_sink"],["cairn.tui.render",33,"from .render import render_trace_text"],["cairn.core",574,"from cairn.core import reset_sink, reset_store"],["cairn.core",406,"from cairn.core import from_jsonable"],["cairn.tui.widgets",35,"from .widgets import ChoicePanel, ConfirmPanel"],["cairn.run.spans",24,"from cairn.run.spans import SpanGraph"],["cairn.interaction",575,"from cairn.interaction import reset_interaction_sink"],["cairn.run",23,"from cairn.run import RunInfo, RunManager, SymlinkTracker, list_runs"],["cairn.tui.messages",26,"from .messages import ("]],"cairn":[["cairn.run.gc",16,"from cairn.run import ("],["cairn.core",3,"from cairn.core import ("],["cairn.core.step",3,"from cairn.core import ("],["cairn.run",16,"from cairn.run import ("]],"cairn.cli":[["cairn.run",36,"from cairn.run import show_trace"],["cairn.tui",145,"from cairn.tui import browse"],["cairn.run",64,"from cairn.run import show_runs"],["cairn.run.gc",52,"from cairn.run import gc, list_runs"],["cairn.tui",128,"from cairn.tui import run_app"],["cairn.run",30,"from cairn.run import show_runs"],["cairn.run",149,"from cairn.run import show_runs"],["cairn.run",52,"from cairn.run import gc, list_runs"],["cairn.run",80,"from cairn.run import run as cairn_run"],["cairn.run",119,"from cairn.run import gc_outputs, list_runs, remove_run"],["cairn.run",43,"from cairn.run import show_output"]],"cairn.ext":[],"cairn.interaction":[["cairn.core.step",39,"from cairn.core import step"],["cairn.core.context",40,"from cairn.core.context import current_span"]],"cairn.core.hash":[["cairn.core.types",129,"from .types import StepInfo"]],"cairn.core.types":[["cairn.core.hash",16,"from .hash import compute_cairn_id, resolve_hashable"]],"cairn.tui.render":[["cairn.run.show",9,"from cairn.run.show import TRACE_RESERVED, format_cost"]],"cairn.core":[["cairn.core.step",8,"from .step import ("],["cairn.core.sink",45,"from .sink import CompositeSink, JSONLSink, event_to_dict"],["cairn.core.hash",30,"from .hash import ("],["cairn.core.store",46,"from .store import FileStore, MemoryStore, OverlayStore, Store, StoreStats"],["cairn.core.context",18,"from .context import ("],["cairn.core.serial",38,"from .serial import ("],["cairn.core.types",47,"from .types import CacheEntry, SpanMetrics, StepInfo, TaskSpan, TraceRecord"],["cairn.core.patterns",37,"from .patterns import rate_limited, replayable"]],"cairn.core.context":[["cairn.core.types",11,"from .types import TaskSpan"]],"cairn.core.lock":[],"cairn.tui":[["cairn.tui.app",18,"from .app import CairnApp"]],"cairn.core.step":[["cairn.core.types",20,"from .types import CacheEntry, SpanMetrics, StepInfo, TaskSpan, TraceRecord"],["cairn.core.store",12,"from .store import ("],["cairn.core.context",11,"from .context import current_span, emit_event, next_id"]],"cairn.core.testing":[["cairn.core",9,"from cairn.core import set_store"],["cairn.core.store",11,"from .store import MemoryStore"],["cairn.core.context",8,"from .context import Event, MemorySink, reset_id_counter, set_sink"],["cairn.core.context",131,"from .context import reset_sink"],["cairn.core.hash",10,"from .hash import clear_hash_funcs, set_hash_funcs"],["cairn.core.step",130,"from .step import reset_store"]],"cairn.tui.widgets":[],"cairn.tui.sinks":[["cairn.tui.messages",20,"from .messages import ("],["cairn.core",18,"from cairn.core import Event, event_to_dict"]],"cairn.run.gc":[["cairn.core.lock",15,"from cairn.core.lock import gc_exclusive as _gc_exclusive"]],"cairn.ext.claude":[["cairn",16,"from cairn import trace"]],"cairn.core.store":[["cairn.core.serial",20,"from .serial import from_jsonable, to_jsonable"],["cairn.core.types",21,"from .types import CacheEntry, TraceRecord"],["cairn.core.lock",19,"from .lock import store_shared"]],"cairn.run":[["cairn.core",175,"from cairn.core import reset_sink, reset_store # noqa: PLC0415"],["cairn.run.gc",185,"from .gc import ( # noqa: E402"],["cairn.run.show",193,"from .show import show_output, show_runs, show_trace # noqa: E402"],["cairn.run.spans",194,"from .spans import SpanGraph # noqa: E402"],["cairn.core",12,"from cairn.core import Event, FileStore, Handle, JSONLSink, OverlayStore, set_sink, set_store"]],"cairn.core.serial":[],"cairn.core.patterns":[["cairn.core.step",10,"from .step import Handle, cached_output, cached_tracing, step, trace"],["cairn.core.types",11,"from .types import StepInfo, TraceRecord"]],"cairn.core.sink":[["cairn.core.context",10,"from .context import Event"]]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"cairn.core.patterns": 1776711829.4129565, "cairn.tui.widgets": 1776725754.021825, "cairn.ext": 1777061802.32637, "cairn.run": 1777065258.432119, "cairn.run.show": 1777065290.855728, "cairn.tui": 1776729888.950072, "cairn.tui.sinks": 1776729888.9504905, "cairn.interaction": 1776730680.4906645, "cairn.core": 1777065234.2434435, "cairn.tui.render": 1776729888.9503968, "cairn.core.step": 1777068889.3958, "cairn.cli": 1776729888.9499216, "cairn.core.sink": 1777066204.0551448, "cairn.core.types": 1777064789.6603053, "cairn.run.gc": 1777065279.6702857, "cairn.run.spans": 1777065135.7066474, "cairn.tui.app": 1777066213.3082044, "cairn.core.serial": 1777064703.2457192, "cairn.core.lock": 1777062187.6831, "cairn": 1776697640.5956903, "cairn.tui.messages": 1776729888.950324, "cairn.ext.claude": 1777062502.825604, "cairn.core.hash": 1777064796.0770473, "cairn.core.store": 1777068851.1281378, "cairn.core.context": 1777066220.2757235, "cairn.core.testing": 1776689957.1186948}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"cairns.run.spans": 1777908586.6772733, "cairns.core.serial": 1777908821.6975274, "cairns.run": 1777909145.351873, "cairns.ext.claude": 1777908738.108794, "cairns.core.runtime": 1777910918.4830177, "cairns.core.step": 1777910982.8763356, "cairns.tui": 1777910918.484366, "cairns.core.sink": 1777892323.3431523, "cairns.testing": 1777909069.6382208, "cairns.tui.widgets": 1776725754.021825, "cairns.core.lock": 1777910918.4838786, "cairns.patterns.semver": 1777911005.908118, "cairns.core.hash": 1777908821.7029088, "cairns.patterns": 1777908738.0626025, "cairns.core.types": 1777908236.6348233, "cairns.ext": 1777061802.32637, "cairns.core.store": 1777909027.1370761, "cairns.tui.app": 1777908738.0930882, "cairns": 1777908848.0384898, "cairns.cli": 1777911082.101685, "cairns.tui.messages": 1776729888.950324, "cairns.run.show": 1777909027.149721, "cairns.tui.render": 1777908738.0466282, "cairns.core.cairn": 1777910989.1823654, "cairns.interaction": 1777908738.1828606, "cairns.tui.sinks": 1777908738.0774064, "cairns.run.gc": 1777910918.4849072, "cairns.core": 1777910959.412853}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"cairns.tui.app":[["cairns.core",407,"from cairns.core import from_jsonable"],["cairns.tui.messages",27,"from .messages import ("],["cairns.run",24,"from cairns.run import _make_run_dir, _update_latest # noqa: PLC2701"],["cairns.tui.widgets",36,"from .widgets import ChoicePanel, ConfirmPanel"],["cairns.run.spans",25,"from cairns.run.spans import SpanGraph"],["cairns.run",23,"from cairns.run import RunDirSink, RunInfo, list_runs"],["cairns.tui.render",34,"from .render import render_trace_text"],["cairns.core",21,"from cairns.core import CompositeSink, FileStore, Handle"],["cairns.tui.sinks",35,"from .sinks import TuiInteractionSink, TuiSink"],["cairns.core.runtime",22,"from cairns.core.runtime import Run"]],"cairns.core.step":[["cairns.core.runtime",11,"from .runtime import current_run, current_span, emit_event"],["cairns.core.cairn",672,"from cairns.core.cairn import Cairn # noqa: PLC0415"],["cairns.core.types",19,"from .types import Record, SpanMetrics, StepInfo, TaskSpan, TraceRecord"],["cairns.core.store",12,"from .store import ("]],"cairns.run.spans":[],"cairns.patterns":[["cairns.core.types",11,"from cairns.core.types import StepInfo, TraceRecord"],["cairns.core.step",10,"from cairns.core.step import Handle, cached_output, cached_tracing, step, trace"]],"cairns.interaction":[["cairns.core.step",29,"from cairns.core import step"],["cairns.core.runtime",30,"from cairns.core.runtime import InteractionSink, current_run, current_span"]],"cairns.ext.claude":[["cairns",16,"from cairns import trace"]],"cairns.core.serial":[["cairns.core.runtime",44,"from .runtime import active_serializers # noqa: PLC0415"]],"cairns.core.types":[["cairns.core.hash",16,"from .hash import compute_cairn_id, resolve_hashable"]],"cairns.patterns.semver":[["cairns.core",23,"from cairns.core import Cairn, Record"]],"cairns.core.store":[["cairns.core.lock",19,"from .lock import store_shared"],["cairns.core.serial",20,"from .serial import from_jsonable, to_jsonable"],["cairns.core.types",21,"from .types import Record, TraceRecord"]],"cairns.tui.widgets":[],"cairns.tui.messages":[],"cairns":[["cairns.core",3,"from cairns.core import ("],["cairns.core.step",3,"from cairns.core import ("],["cairns.run.gc",17,"from cairns.run import ("],["cairns.run",17,"from cairns.run import ("],["cairns.patterns",16,"from cairns.patterns import rate_limited, replayable"],["cairns.core.cairn",3,"from cairns.core import ("]],"cairns.core.lock":[],"cairns.run.show":[["cairns.core",174,"from cairns.core import event_to_dict"],["cairns.run.gc",17,"from .gc import list_runs"],["cairns.core",16,"from cairns.core import Event"],["cairns.run.spans",18,"from .spans import SpanGraph"]],"cairns.core":[["cairns.patterns",33,"from cairns.patterns import rate_limited, replayable"],["cairns.core.cairn",28,"from .cairn import Cairn, cairn # noqa: F401"],["cairns.core.runtime",15,"from .runtime import ("],["cairns.core.sink",39,"from .sink import CompositeSink, JSONLSink, event_to_dict"],["cairns.core.store",40,"from .store import FileStore, MemoryStore, OverlayStore, Store, StoreStats"],["cairns.core.hash",29,"from .hash import ("],["cairns.core.serial",34,"from .serial import ("],["cairns.core.types",41,"from .types import Record, SpanMetrics, StepInfo, TaskSpan, TraceRecord"],["cairns.core.step",8,"from .step import ("]],"cairns.run.gc":[["cairns.core.lock",15,"from cairns.core.lock import gc_exclusive as _gc_exclusive"]],"cairns.ext":[],"cairns.core.sink":[["cairns.core.runtime",10,"from .runtime import Event"]],"cairns.tui.sinks":[["cairns.core",18,"from cairns.core import Event, event_to_dict"],["cairns.tui.messages",20,"from .messages import ("]],"cairns.run":[["cairns.core",11,"from cairns.core import Event, Handle, JSONLSink, OverlayStore"],["cairns.run.gc",246,"from .gc import ( # noqa: E402"],["cairns.core.runtime",12,"from cairns.core.runtime import ("],["cairns.run.show",254,"from .show import show_output, show_runs, show_trace # noqa: E402"],["cairns.run.spans",255,"from .spans import SpanGraph # noqa: E402"]],"cairns.core.cairn":[["cairns.core.store",25,"from .store import Store"],["cairns.core.runtime",21,"from .runtime import current_run, current_span"],["cairns.core.types",22,"from .types import Record"]],"cairns.tui.render":[["cairns.run.show",9,"from cairns.run.show import TRACE_RESERVED, format_cost"]],"cairns.testing":[["cairns.core.runtime",8,"from cairns.core.runtime import Runtime, Event, MemorySink, Run"],["cairns.core.store",9,"from cairns.core.store import MemoryStore"]],"cairns.core.hash":[["cairns.core.runtime",90,"from .runtime import active_hash_funcs # noqa: PLC0415"],["cairns.core.types",143,"from .types import StepInfo"]],"cairns.tui":[["cairns.tui.app",18,"from .app import CairnsApp"]],"cairns.core.runtime":[["cairns.run",245,"from cairns.run import arun as _arun # noqa: PLC0415"],["cairns.testing",259,"from cairns.testing import Harness # noqa: PLC0415"],["cairns.core.hash",178,"from .hash import install_defaults as install_hash_defaults # noqa: PLC0415"],["cairns.core.types",33,"from .types import TaskSpan"],["cairns.core.serial",179,"from .serial import install_defaults as install_serial_defaults # noqa: PLC0415"],["cairns.core.store",170,"from .store import FileStore # noqa: PLC0415"],["cairns.core.serial",36,"from .serial import Serializer"],["cairns.core.store",37,"from .store import Store"],["cairns.run",217,"from cairns.run import run as _execute # noqa: PLC0415"]],"cairns.cli":[["cairns.run",80,"from cairns.run import run as cairn_run"],["cairns.run",30,"from cairns.run import show_runs"],["cairns.run",43,"from cairns.run import show_output"],["cairns.run",36,"from cairns.run import show_trace"],["cairns.run",149,"from cairns.run import show_runs"],["cairns.run",119,"from cairns.run import gc_outputs, list_runs, remove_run"],["cairns.run",52,"from cairns.run import gc, list_runs"],["cairns.tui",145,"from cairns.tui import browse"],["cairns.tui",128,"from cairns.tui import run_app"],["cairns.run",64,"from cairns.run import show_runs"],["cairns.run.gc",52,"from cairns.run import gc, list_runs"]]}
|
cairns-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mathijs Henquet
|
|
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.
|
cairns-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cairns
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Compute graph orchestration with caching and observability
|
|
5
|
+
Project-URL: Homepage, https://github.com/mathijshenquet/cairns
|
|
6
|
+
Project-URL: Repository, https://github.com/mathijshenquet/cairns
|
|
7
|
+
Project-URL: Issues, https://github.com/mathijshenquet/cairns/issues
|
|
8
|
+
Author-email: Mathijs Henquet <mathijs.henquet@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,async,caching,compute-graph,llm,observability,pipeline,tracing,workflow
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.12
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: import-linter>=2.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: pydantic>=2.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-asyncio>=0.25.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: rich>=13.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: textual>=3.0; extra == 'dev'
|
|
30
|
+
Provides-Extra: full
|
|
31
|
+
Requires-Dist: pydantic>=2.0; extra == 'full'
|
|
32
|
+
Requires-Dist: rich>=13.0; extra == 'full'
|
|
33
|
+
Requires-Dist: textual>=3.0; extra == 'full'
|
|
34
|
+
Provides-Extra: pydantic
|
|
35
|
+
Requires-Dist: pydantic>=2.0; extra == 'pydantic'
|
|
36
|
+
Provides-Extra: semver
|
|
37
|
+
Requires-Dist: semver>=3.0; extra == 'semver'
|
|
38
|
+
Provides-Extra: tui
|
|
39
|
+
Requires-Dist: rich>=13.0; extra == 'tui'
|
|
40
|
+
Requires-Dist: textual>=3.0; extra == 'tui'
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
|
|
43
|
+
<p align="center">
|
|
44
|
+
<img src="https://raw.githubusercontent.com/mathijshenquet/cairns/main/docs/logo.jpg" alt="Cairn" width="600">
|
|
45
|
+
</p>
|
|
46
|
+
|
|
47
|
+
# Cairns
|
|
48
|
+
|
|
49
|
+
**A microframework for compute graphs with caching, tracing, and replay.**
|
|
50
|
+
|
|
51
|
+
Think *PyTorch for agent pipelines* — though nothing about it is agent-specific.
|
|
52
|
+
You write ordinary async Python; a `@step` decorator turns each function into a
|
|
53
|
+
tracked, and optionally cached node in a graph that emerges from execution instead of being
|
|
54
|
+
declared up front.
|
|
55
|
+
|
|
56
|
+
> **Alpha.** Public API names, the on-disk cache format, and the higher-order
|
|
57
|
+
> wrappers may still change between minor versions. Pin a version if you
|
|
58
|
+
> depend on it.
|
|
59
|
+
|
|
60
|
+
## Why
|
|
61
|
+
|
|
62
|
+
Declarative graph frameworks (LangGraph, CrewAI, Airflow-style DAGs) force you
|
|
63
|
+
into their node/edge DSL. Cairn goes the other way: the graph **is** your code,
|
|
64
|
+
the framework just instruments it. From that you get:
|
|
65
|
+
|
|
66
|
+
- **Caching** keyed on `(function identity, body version, resolved args)` — change
|
|
67
|
+
one function, only its downstream re-executes.
|
|
68
|
+
- **Tracing** — live, structured event log; a built-in TUI renders it.
|
|
69
|
+
- **Resume** — a failed pipeline reruns from the last successful step.
|
|
70
|
+
- **Replay** — cached runs can replay with original timing, indistinguishable
|
|
71
|
+
from a live execution.
|
|
72
|
+
- **Human-in-the-loop** as a regular async step (`await_input(...)`).
|
|
73
|
+
|
|
74
|
+
Works for LLM pipelines, scrapers, ETL, long-running research — anything that
|
|
75
|
+
fits into mostly-pure async functions.
|
|
76
|
+
|
|
77
|
+
## Install
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
pip install cairns[tui] # TUI is worth having
|
|
81
|
+
pip install cairns[full] # TUI + pydantic hashing
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or with `uv`:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
uv add cairns --extra tui
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Requires Python 3.12+.
|
|
91
|
+
|
|
92
|
+
## Hello world
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
import asyncio
|
|
96
|
+
from cairns import step, run, trace
|
|
97
|
+
|
|
98
|
+
@step(memo=True)
|
|
99
|
+
async def fetch(url: str) -> str:
|
|
100
|
+
trace("fetching", state="running")
|
|
101
|
+
await asyncio.sleep(0.2) # pretend HTTP
|
|
102
|
+
return f"<html>{url}</html>"
|
|
103
|
+
|
|
104
|
+
@step
|
|
105
|
+
async def extract(html: str) -> int:
|
|
106
|
+
return len(html)
|
|
107
|
+
|
|
108
|
+
@step
|
|
109
|
+
async def pipeline(urls: list[str]) -> list[int]:
|
|
110
|
+
pages = [fetch(u) for u in urls] # returns Handles; runs concurrently
|
|
111
|
+
return [await extract(p) for p in pages] # pages are awaited inside extract
|
|
112
|
+
|
|
113
|
+
run(pipeline, store_path=".cairns",
|
|
114
|
+
args=(["https://a", "https://b", "https://c"],))
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Run it twice. The second run is instant — every `@step(memo=True)` result is
|
|
118
|
+
looked up by cache key. Edit the body of `extract`, rerun: only `extract`
|
|
119
|
+
re-executes, fetches are cache hits.
|
|
120
|
+
|
|
121
|
+
## CLI
|
|
122
|
+
|
|
123
|
+
```sh
|
|
124
|
+
cairns examples/research_fake_llm.py # run the pipeline, opens TUI if installed
|
|
125
|
+
cairns examples/research_fake_llm.py slow # run the `slow` entry point
|
|
126
|
+
cairns examples/research_fake_llm.py -f # clear this entry's cache, then run
|
|
127
|
+
cairns # interactive run browser over past runs
|
|
128
|
+
cairns list # flat list of runs
|
|
129
|
+
cairns show [RUN_ID] # print a trace (latest if omitted)
|
|
130
|
+
cairns gc [--before YYYY-MM-DD] # garbage-collect old runs
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Default store is `./.cairns/`. Override with `--store PATH` (or `-s`). Entry
|
|
134
|
+
points default to a function named `main`; pass a second positional arg to
|
|
135
|
+
pick another, e.g. `cairns script.py my_pipeline`.
|
|
136
|
+
|
|
137
|
+
## Examples
|
|
138
|
+
|
|
139
|
+
Each example runs standalone with `python examples/<name>.py`, or through the
|
|
140
|
+
CLI with `cairns examples/<name>.py` for the TUI.
|
|
141
|
+
|
|
142
|
+
| Example | What it shows |
|
|
143
|
+
|---------|---------------|
|
|
144
|
+
| [`scraper.py`](https://github.com/mathijshenquet/cairns/blob/main/examples/scraper.py) | Fan-out + chain, non-AI, fully mocked. Good first look. |
|
|
145
|
+
| [`failure_resume.py`](https://github.com/mathijshenquet/cairns/blob/main/examples/failure_resume.py) | A step fails on item 3; rerun resumes from cache. |
|
|
146
|
+
| [`research_fake_llm.py`](https://github.com/mathijshenquet/cairns/blob/main/examples/research_fake_llm.py) | Fan-out across N subjects, retry loop, rate limiting, simulated 20% API failure rate. No API key needed. |
|
|
147
|
+
| [`hitl.py`](https://github.com/mathijshenquet/cairns/blob/main/examples/hitl.py) | `await_input` inside a step — TUI input widget, stdin fallback. |
|
|
148
|
+
| [`research_haiku.py`](https://github.com/mathijshenquet/cairns/blob/main/examples/research_haiku.py) + [`claude.py`](https://github.com/mathijshenquet/cairns/blob/main/examples/claude.py) | Live research over real AI companies via the `claude` CLI (Haiku). Cached by ISO week — re-runs within the week are free. |
|
|
149
|
+
|
|
150
|
+
## What you'll see
|
|
151
|
+
|
|
152
|
+
With `cairns[tui]` installed, the CLI opens a live span tree: each `@step`
|
|
153
|
+
invocation is a row, child steps indent, `trace(...)` calls attach as
|
|
154
|
+
annotations, and `cost={...}` kwargs get summed up the tree. Failures colour
|
|
155
|
+
red, running steps pulse, completed steps show wall time + own time (excluding
|
|
156
|
+
waits on children).
|
|
157
|
+
|
|
158
|
+
Without the TUI, the same events stream to `.cairns/runs/{entry}-{ts}/trace.jsonl`
|
|
159
|
+
and you can read them with `cairns show`.
|
|
160
|
+
|
|
161
|
+
## Docs
|
|
162
|
+
|
|
163
|
+
- [`docs/motivation.md`](https://github.com/mathijshenquet/cairns/blob/main/docs/motivation.md) — the problem and the analogy to PyTorch.
|
|
164
|
+
- [`docs/design.md`](https://github.com/mathijshenquet/cairns/blob/main/docs/design.md) — all primitives, event log, stores, plugin points.
|
|
165
|
+
- [`docs/patterns.md`](https://github.com/mathijshenquet/cairns/blob/main/docs/patterns.md) — comparison against Prefect, LangGraph, Temporal, Flyte, CrewAI across seven patterns.
|
|
166
|
+
|
|
167
|
+
## Status
|
|
168
|
+
|
|
169
|
+
Alpha, but the core works:
|
|
170
|
+
|
|
171
|
+
- `@step`, `Handle`, `trace`, `cached_output/tracing`, `replayable`, `rate_limited`, `await_input` all shipped.
|
|
172
|
+
- File-backed content-addressed store, JSONL trace sink, symlinked run layout, GC.
|
|
173
|
+
- Live TUI for span-tree viewing.
|
|
174
|
+
- 110 tests, covering core + hashing + disk + resume + GC + metrics + patterns.
|
|
175
|
+
|
|
176
|
+
Possible future work: a web UI, distributed execution, distributed cache store.
|
|
177
|
+
|
|
178
|
+
Feedback and breakage reports welcome via issues.
|
cairns-0.2.0/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/mathijshenquet/cairns/main/docs/logo.jpg" alt="Cairn" width="600">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# Cairns
|
|
6
|
+
|
|
7
|
+
**A microframework for compute graphs with caching, tracing, and replay.**
|
|
8
|
+
|
|
9
|
+
Think *PyTorch for agent pipelines* — though nothing about it is agent-specific.
|
|
10
|
+
You write ordinary async Python; a `@step` decorator turns each function into a
|
|
11
|
+
tracked, and optionally cached node in a graph that emerges from execution instead of being
|
|
12
|
+
declared up front.
|
|
13
|
+
|
|
14
|
+
> **Alpha.** Public API names, the on-disk cache format, and the higher-order
|
|
15
|
+
> wrappers may still change between minor versions. Pin a version if you
|
|
16
|
+
> depend on it.
|
|
17
|
+
|
|
18
|
+
## Why
|
|
19
|
+
|
|
20
|
+
Declarative graph frameworks (LangGraph, CrewAI, Airflow-style DAGs) force you
|
|
21
|
+
into their node/edge DSL. Cairn goes the other way: the graph **is** your code,
|
|
22
|
+
the framework just instruments it. From that you get:
|
|
23
|
+
|
|
24
|
+
- **Caching** keyed on `(function identity, body version, resolved args)` — change
|
|
25
|
+
one function, only its downstream re-executes.
|
|
26
|
+
- **Tracing** — live, structured event log; a built-in TUI renders it.
|
|
27
|
+
- **Resume** — a failed pipeline reruns from the last successful step.
|
|
28
|
+
- **Replay** — cached runs can replay with original timing, indistinguishable
|
|
29
|
+
from a live execution.
|
|
30
|
+
- **Human-in-the-loop** as a regular async step (`await_input(...)`).
|
|
31
|
+
|
|
32
|
+
Works for LLM pipelines, scrapers, ETL, long-running research — anything that
|
|
33
|
+
fits into mostly-pure async functions.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
pip install cairns[tui] # TUI is worth having
|
|
39
|
+
pip install cairns[full] # TUI + pydantic hashing
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or with `uv`:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
uv add cairns --extra tui
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Requires Python 3.12+.
|
|
49
|
+
|
|
50
|
+
## Hello world
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
import asyncio
|
|
54
|
+
from cairns import step, run, trace
|
|
55
|
+
|
|
56
|
+
@step(memo=True)
|
|
57
|
+
async def fetch(url: str) -> str:
|
|
58
|
+
trace("fetching", state="running")
|
|
59
|
+
await asyncio.sleep(0.2) # pretend HTTP
|
|
60
|
+
return f"<html>{url}</html>"
|
|
61
|
+
|
|
62
|
+
@step
|
|
63
|
+
async def extract(html: str) -> int:
|
|
64
|
+
return len(html)
|
|
65
|
+
|
|
66
|
+
@step
|
|
67
|
+
async def pipeline(urls: list[str]) -> list[int]:
|
|
68
|
+
pages = [fetch(u) for u in urls] # returns Handles; runs concurrently
|
|
69
|
+
return [await extract(p) for p in pages] # pages are awaited inside extract
|
|
70
|
+
|
|
71
|
+
run(pipeline, store_path=".cairns",
|
|
72
|
+
args=(["https://a", "https://b", "https://c"],))
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Run it twice. The second run is instant — every `@step(memo=True)` result is
|
|
76
|
+
looked up by cache key. Edit the body of `extract`, rerun: only `extract`
|
|
77
|
+
re-executes, fetches are cache hits.
|
|
78
|
+
|
|
79
|
+
## CLI
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
cairns examples/research_fake_llm.py # run the pipeline, opens TUI if installed
|
|
83
|
+
cairns examples/research_fake_llm.py slow # run the `slow` entry point
|
|
84
|
+
cairns examples/research_fake_llm.py -f # clear this entry's cache, then run
|
|
85
|
+
cairns # interactive run browser over past runs
|
|
86
|
+
cairns list # flat list of runs
|
|
87
|
+
cairns show [RUN_ID] # print a trace (latest if omitted)
|
|
88
|
+
cairns gc [--before YYYY-MM-DD] # garbage-collect old runs
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Default store is `./.cairns/`. Override with `--store PATH` (or `-s`). Entry
|
|
92
|
+
points default to a function named `main`; pass a second positional arg to
|
|
93
|
+
pick another, e.g. `cairns script.py my_pipeline`.
|
|
94
|
+
|
|
95
|
+
## Examples
|
|
96
|
+
|
|
97
|
+
Each example runs standalone with `python examples/<name>.py`, or through the
|
|
98
|
+
CLI with `cairns examples/<name>.py` for the TUI.
|
|
99
|
+
|
|
100
|
+
| Example | What it shows |
|
|
101
|
+
|---------|---------------|
|
|
102
|
+
| [`scraper.py`](https://github.com/mathijshenquet/cairns/blob/main/examples/scraper.py) | Fan-out + chain, non-AI, fully mocked. Good first look. |
|
|
103
|
+
| [`failure_resume.py`](https://github.com/mathijshenquet/cairns/blob/main/examples/failure_resume.py) | A step fails on item 3; rerun resumes from cache. |
|
|
104
|
+
| [`research_fake_llm.py`](https://github.com/mathijshenquet/cairns/blob/main/examples/research_fake_llm.py) | Fan-out across N subjects, retry loop, rate limiting, simulated 20% API failure rate. No API key needed. |
|
|
105
|
+
| [`hitl.py`](https://github.com/mathijshenquet/cairns/blob/main/examples/hitl.py) | `await_input` inside a step — TUI input widget, stdin fallback. |
|
|
106
|
+
| [`research_haiku.py`](https://github.com/mathijshenquet/cairns/blob/main/examples/research_haiku.py) + [`claude.py`](https://github.com/mathijshenquet/cairns/blob/main/examples/claude.py) | Live research over real AI companies via the `claude` CLI (Haiku). Cached by ISO week — re-runs within the week are free. |
|
|
107
|
+
|
|
108
|
+
## What you'll see
|
|
109
|
+
|
|
110
|
+
With `cairns[tui]` installed, the CLI opens a live span tree: each `@step`
|
|
111
|
+
invocation is a row, child steps indent, `trace(...)` calls attach as
|
|
112
|
+
annotations, and `cost={...}` kwargs get summed up the tree. Failures colour
|
|
113
|
+
red, running steps pulse, completed steps show wall time + own time (excluding
|
|
114
|
+
waits on children).
|
|
115
|
+
|
|
116
|
+
Without the TUI, the same events stream to `.cairns/runs/{entry}-{ts}/trace.jsonl`
|
|
117
|
+
and you can read them with `cairns show`.
|
|
118
|
+
|
|
119
|
+
## Docs
|
|
120
|
+
|
|
121
|
+
- [`docs/motivation.md`](https://github.com/mathijshenquet/cairns/blob/main/docs/motivation.md) — the problem and the analogy to PyTorch.
|
|
122
|
+
- [`docs/design.md`](https://github.com/mathijshenquet/cairns/blob/main/docs/design.md) — all primitives, event log, stores, plugin points.
|
|
123
|
+
- [`docs/patterns.md`](https://github.com/mathijshenquet/cairns/blob/main/docs/patterns.md) — comparison against Prefect, LangGraph, Temporal, Flyte, CrewAI across seven patterns.
|
|
124
|
+
|
|
125
|
+
## Status
|
|
126
|
+
|
|
127
|
+
Alpha, but the core works:
|
|
128
|
+
|
|
129
|
+
- `@step`, `Handle`, `trace`, `cached_output/tracing`, `replayable`, `rate_limited`, `await_input` all shipped.
|
|
130
|
+
- File-backed content-addressed store, JSONL trace sink, symlinked run layout, GC.
|
|
131
|
+
- Live TUI for span-tree viewing.
|
|
132
|
+
- 110 tests, covering core + hashing + disk + resume + GC + metrics + patterns.
|
|
133
|
+
|
|
134
|
+
Possible future work: a web UI, distributed execution, distributed cache store.
|
|
135
|
+
|
|
136
|
+
Feedback and breakage reports welcome via issues.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
version: "3"
|
|
2
|
+
|
|
3
|
+
dotenv: [".env"]
|
|
4
|
+
|
|
5
|
+
tasks:
|
|
6
|
+
build:
|
|
7
|
+
desc: Build sdist + wheel into dist/
|
|
8
|
+
cmds:
|
|
9
|
+
- rm -rf dist/
|
|
10
|
+
- uv build
|
|
11
|
+
|
|
12
|
+
check:
|
|
13
|
+
desc: Validate built artifacts (twine check)
|
|
14
|
+
deps: [build]
|
|
15
|
+
cmds:
|
|
16
|
+
- uvx twine check dist/*
|
|
17
|
+
|
|
18
|
+
publish:
|
|
19
|
+
desc: Publish to PyPI. Reads $PYPI_TOKEN from env (loaded from .env via devenv).
|
|
20
|
+
deps: [check]
|
|
21
|
+
preconditions:
|
|
22
|
+
- sh: 'test -n "$PYPI_TOKEN"'
|
|
23
|
+
msg: "PYPI_TOKEN is not set. Add it to .env (devenv auto-loads dotenv)."
|
|
24
|
+
cmds:
|
|
25
|
+
- UV_PUBLISH_TOKEN="$PYPI_TOKEN" uv publish
|
|
26
|
+
|
|
27
|
+
publish:test:
|
|
28
|
+
desc: Publish to TestPyPI. Reads $PYPI_TEST_TOKEN (or $PYPI_TOKEN as fallback).
|
|
29
|
+
deps: [check]
|
|
30
|
+
preconditions:
|
|
31
|
+
- sh: 'test -n "$PYPI_TEST_TOKEN" -o -n "$PYPI_TOKEN"'
|
|
32
|
+
msg: "Set PYPI_TEST_TOKEN (or PYPI_TOKEN) in .env."
|
|
33
|
+
cmds:
|
|
34
|
+
- UV_PUBLISH_TOKEN="${PYPI_TEST_TOKEN:-$PYPI_TOKEN}" uv publish --publish-url https://test.pypi.org/legacy/
|
cairns-0.2.0/devenv.lock
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"nodes": {
|
|
3
|
+
"devenv": {
|
|
4
|
+
"locked": {
|
|
5
|
+
"dir": "src/modules",
|
|
6
|
+
"lastModified": 1776718099,
|
|
7
|
+
"narHash": "sha256-JAR6x8Au4xxk6X7ijhlYETQg0F0OS0ihZ5ARiguDclc=",
|
|
8
|
+
"owner": "cachix",
|
|
9
|
+
"repo": "devenv",
|
|
10
|
+
"rev": "034b677ee035a29a077ecbaadfb2908719272919",
|
|
11
|
+
"type": "github"
|
|
12
|
+
},
|
|
13
|
+
"original": {
|
|
14
|
+
"dir": "src/modules",
|
|
15
|
+
"owner": "cachix",
|
|
16
|
+
"repo": "devenv",
|
|
17
|
+
"type": "github"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"flake-compat": {
|
|
21
|
+
"flake": false,
|
|
22
|
+
"locked": {
|
|
23
|
+
"lastModified": 1767039857,
|
|
24
|
+
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
|
25
|
+
"owner": "edolstra",
|
|
26
|
+
"repo": "flake-compat",
|
|
27
|
+
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
|
28
|
+
"type": "github"
|
|
29
|
+
},
|
|
30
|
+
"original": {
|
|
31
|
+
"owner": "edolstra",
|
|
32
|
+
"repo": "flake-compat",
|
|
33
|
+
"type": "github"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"nixpkgs": {
|
|
37
|
+
"inputs": {
|
|
38
|
+
"nixpkgs-src": "nixpkgs-src"
|
|
39
|
+
},
|
|
40
|
+
"locked": {
|
|
41
|
+
"lastModified": 1776348333,
|
|
42
|
+
"narHash": "sha256-ZyrYhlLGGuN5ieW7pE65meJJYnNz5el9vJtGBEJyDMQ=",
|
|
43
|
+
"owner": "cachix",
|
|
44
|
+
"repo": "devenv-nixpkgs",
|
|
45
|
+
"rev": "efff47329167854ce48541c7ef731bf120753c7e",
|
|
46
|
+
"type": "github"
|
|
47
|
+
},
|
|
48
|
+
"original": {
|
|
49
|
+
"owner": "cachix",
|
|
50
|
+
"ref": "rolling",
|
|
51
|
+
"repo": "devenv-nixpkgs",
|
|
52
|
+
"type": "github"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"nixpkgs-python": {
|
|
56
|
+
"inputs": {
|
|
57
|
+
"flake-compat": "flake-compat",
|
|
58
|
+
"nixpkgs": [
|
|
59
|
+
"nixpkgs"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"locked": {
|
|
63
|
+
"lastModified": 1774026014,
|
|
64
|
+
"narHash": "sha256-UBBQYhyAKayDCi6iCIKShQXWRvwyj5omLPOuSeVjtOY=",
|
|
65
|
+
"owner": "cachix",
|
|
66
|
+
"repo": "nixpkgs-python",
|
|
67
|
+
"rev": "8b6d4103312761d4144b7bf9aebcc2f394b7e325",
|
|
68
|
+
"type": "github"
|
|
69
|
+
},
|
|
70
|
+
"original": {
|
|
71
|
+
"owner": "cachix",
|
|
72
|
+
"repo": "nixpkgs-python",
|
|
73
|
+
"type": "github"
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"nixpkgs-src": {
|
|
77
|
+
"flake": false,
|
|
78
|
+
"locked": {
|
|
79
|
+
"lastModified": 1775888245,
|
|
80
|
+
"narHash": "sha256-nwASzrRDD1JBEu/o8ekKYEXm/oJW6EMCzCRdrwcLe90=",
|
|
81
|
+
"owner": "NixOS",
|
|
82
|
+
"repo": "nixpkgs",
|
|
83
|
+
"rev": "13043924aaa7375ce482ebe2494338e058282925",
|
|
84
|
+
"type": "github"
|
|
85
|
+
},
|
|
86
|
+
"original": {
|
|
87
|
+
"owner": "NixOS",
|
|
88
|
+
"ref": "nixpkgs-unstable",
|
|
89
|
+
"repo": "nixpkgs",
|
|
90
|
+
"type": "github"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"root": {
|
|
94
|
+
"inputs": {
|
|
95
|
+
"devenv": "devenv",
|
|
96
|
+
"nixpkgs": "nixpkgs",
|
|
97
|
+
"nixpkgs-python": "nixpkgs-python"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"root": "root",
|
|
102
|
+
"version": 7
|
|
103
|
+
}
|
cairns-0.2.0/devenv.nix
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{ pkgs, config, ... }:
|
|
2
|
+
|
|
3
|
+
{
|
|
4
|
+
dotenv.enable = true;
|
|
5
|
+
|
|
6
|
+
languages.python = {
|
|
7
|
+
enable = true;
|
|
8
|
+
uv = {
|
|
9
|
+
enable = true;
|
|
10
|
+
sync.enable = true;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
packages = [
|
|
15
|
+
pkgs.pyright
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
enterShell = ''
|
|
19
|
+
export PATH="${config.devenv.root}/.devenv/state/venv/bin:$PATH"
|
|
20
|
+
'';
|
|
21
|
+
}
|