furu 0.0.1__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.
- furu-0.0.1/.github/workflows/ci.yml +33 -0
- furu-0.0.1/.github/workflows/release.yml +99 -0
- furu-0.0.1/.gitignore +231 -0
- furu-0.0.1/.vscode/settings.json +3 -0
- furu-0.0.1/AGENTS.md +265 -0
- furu-0.0.1/CHANGELOG.md +5 -0
- furu-0.0.1/Makefile +156 -0
- furu-0.0.1/PKG-INFO +502 -0
- furu-0.0.1/README.md +484 -0
- furu-0.0.1/TODO.md +131 -0
- furu-0.0.1/dashboard-frontend/bun.lock +1234 -0
- furu-0.0.1/dashboard-frontend/index.html +21 -0
- furu-0.0.1/dashboard-frontend/orval.config.ts +34 -0
- furu-0.0.1/dashboard-frontend/package-lock.json +7628 -0
- furu-0.0.1/dashboard-frontend/package.json +40 -0
- furu-0.0.1/dashboard-frontend/postcss.config.js +9 -0
- furu-0.0.1/dashboard-frontend/public/favicon.svg +10 -0
- furu-0.0.1/dashboard-frontend/src/api.test.ts +314 -0
- furu-0.0.1/dashboard-frontend/src/components/DAGVisualization.tsx +392 -0
- furu-0.0.1/dashboard-frontend/src/components/EmptyState.tsx +27 -0
- furu-0.0.1/dashboard-frontend/src/components/StatsCard.tsx +63 -0
- furu-0.0.1/dashboard-frontend/src/components/StatusBadge.tsx +40 -0
- furu-0.0.1/dashboard-frontend/src/components/ui/badge.tsx +35 -0
- furu-0.0.1/dashboard-frontend/src/components/ui/button.tsx +56 -0
- furu-0.0.1/dashboard-frontend/src/components/ui/card.tsx +70 -0
- furu-0.0.1/dashboard-frontend/src/components/ui/input.tsx +20 -0
- furu-0.0.1/dashboard-frontend/src/components/ui/table.tsx +96 -0
- furu-0.0.1/dashboard-frontend/src/index.css +78 -0
- furu-0.0.1/dashboard-frontend/src/lib/api-client.ts +26 -0
- furu-0.0.1/dashboard-frontend/src/lib/utils.ts +6 -0
- furu-0.0.1/dashboard-frontend/src/main.tsx +52 -0
- furu-0.0.1/dashboard-frontend/src/routes/__root.tsx +68 -0
- furu-0.0.1/dashboard-frontend/src/routes/dag.tsx +113 -0
- furu-0.0.1/dashboard-frontend/src/routes/experiments.tsx +444 -0
- furu-0.0.1/dashboard-frontend/src/routes/experiments_.$namespace.$furu_hash.tsx +804 -0
- furu-0.0.1/dashboard-frontend/src/routes/index.tsx +195 -0
- furu-0.0.1/dashboard-frontend/tailwind.config.js +41 -0
- furu-0.0.1/dashboard-frontend/tsconfig.json +24 -0
- furu-0.0.1/dashboard-frontend/tsconfig.node.json +14 -0
- furu-0.0.1/dashboard-frontend/vite.config.ts +21 -0
- furu-0.0.1/e2e/bun.lock +21 -0
- furu-0.0.1/e2e/generate_data.py +347 -0
- furu-0.0.1/e2e/global-setup.ts +37 -0
- furu-0.0.1/e2e/global-teardown.ts +14 -0
- furu-0.0.1/e2e/package.json +15 -0
- furu-0.0.1/e2e/playwright.config.ts +66 -0
- furu-0.0.1/e2e/tests/api.spec.ts +182 -0
- furu-0.0.1/e2e/tests/experiments.spec.ts +163 -0
- furu-0.0.1/e2e/tests/home.spec.ts +36 -0
- furu-0.0.1/e2e/tests/navigation.spec.ts +435 -0
- furu-0.0.1/examples/README.md +21 -0
- furu-0.0.1/examples/my_project/__init__.py +3 -0
- furu-0.0.1/examples/my_project/pipelines.py +65 -0
- furu-0.0.1/examples/run_logging.py +26 -0
- furu-0.0.1/examples/run_nested.py +26 -0
- furu-0.0.1/examples/run_train.py +26 -0
- furu-0.0.1/pyproject.toml +56 -0
- furu-0.0.1/src/furu/__init__.py +82 -0
- furu-0.0.1/src/furu/adapters/__init__.py +3 -0
- furu-0.0.1/src/furu/adapters/submitit.py +195 -0
- furu-0.0.1/src/furu/config.py +98 -0
- furu-0.0.1/src/furu/core/__init__.py +4 -0
- furu-0.0.1/src/furu/core/furu.py +999 -0
- furu-0.0.1/src/furu/core/list.py +123 -0
- furu-0.0.1/src/furu/dashboard/__init__.py +9 -0
- furu-0.0.1/src/furu/dashboard/__main__.py +7 -0
- furu-0.0.1/src/furu/dashboard/api/__init__.py +7 -0
- furu-0.0.1/src/furu/dashboard/api/models.py +170 -0
- furu-0.0.1/src/furu/dashboard/api/routes.py +135 -0
- furu-0.0.1/src/furu/dashboard/frontend/dist/assets/index-CbdDfSOZ.css +1 -0
- furu-0.0.1/src/furu/dashboard/frontend/dist/assets/index-DDv_TYB_.js +67 -0
- furu-0.0.1/src/furu/dashboard/frontend/dist/favicon.svg +10 -0
- furu-0.0.1/src/furu/dashboard/frontend/dist/index.html +22 -0
- furu-0.0.1/src/furu/dashboard/main.py +134 -0
- furu-0.0.1/src/furu/dashboard/scanner.py +931 -0
- furu-0.0.1/src/furu/errors.py +76 -0
- furu-0.0.1/src/furu/migrate.py +48 -0
- furu-0.0.1/src/furu/migration.py +926 -0
- furu-0.0.1/src/furu/runtime/__init__.py +27 -0
- furu-0.0.1/src/furu/runtime/env.py +8 -0
- furu-0.0.1/src/furu/runtime/logging.py +301 -0
- furu-0.0.1/src/furu/runtime/tracebacks.py +64 -0
- furu-0.0.1/src/furu/serialization/__init__.py +20 -0
- furu-0.0.1/src/furu/serialization/migrations.py +246 -0
- furu-0.0.1/src/furu/serialization/serializer.py +233 -0
- furu-0.0.1/src/furu/storage/__init__.py +32 -0
- furu-0.0.1/src/furu/storage/metadata.py +282 -0
- furu-0.0.1/src/furu/storage/migration.py +81 -0
- furu-0.0.1/src/furu/storage/state.py +1107 -0
- furu-0.0.1/tasks/migration.md +360 -0
- furu-0.0.1/tests/conftest.py +25 -0
- furu-0.0.1/tests/dashboard/__init__.py +4 -0
- furu-0.0.1/tests/dashboard/conftest.py +448 -0
- furu-0.0.1/tests/dashboard/pipelines.py +192 -0
- furu-0.0.1/tests/dashboard/test_api.py +865 -0
- furu-0.0.1/tests/dashboard/test_scanner.py +786 -0
- furu-0.0.1/tests/test_config.py +10 -0
- furu-0.0.1/tests/test_errors.py +83 -0
- furu-0.0.1/tests/test_furu_core.py +158 -0
- furu-0.0.1/tests/test_furu_inheritance.py +44 -0
- furu-0.0.1/tests/test_furu_inheritance_polymorphic.py +118 -0
- furu-0.0.1/tests/test_furu_list.py +30 -0
- furu-0.0.1/tests/test_furu_typing.py +208 -0
- furu-0.0.1/tests/test_logger.py +141 -0
- furu-0.0.1/tests/test_metadata.py +46 -0
- furu-0.0.1/tests/test_migrations.py +848 -0
- furu-0.0.1/tests/test_raw_dir.py +15 -0
- furu-0.0.1/tests/test_serializer.py +71 -0
- furu-0.0.1/tests/test_state_manager.py +464 -0
- furu-0.0.1/tests/test_submitit_path.py +71 -0
- furu-0.0.1/tests/test_tracebacks.py +5 -0
- furu-0.0.1/uv.lock +745 -0
- furu-0.0.1/version.txt +1 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
test:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
steps:
|
|
10
|
+
- uses: actions/checkout@v4
|
|
11
|
+
|
|
12
|
+
- name: Install uv
|
|
13
|
+
uses: astral-sh/setup-uv@v4
|
|
14
|
+
|
|
15
|
+
- name: Set up Python
|
|
16
|
+
run: uv python install
|
|
17
|
+
|
|
18
|
+
- name: Install bun
|
|
19
|
+
uses: oven-sh/setup-bun@v2
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: |
|
|
23
|
+
uv sync --all-extras
|
|
24
|
+
cd dashboard-frontend && bun install
|
|
25
|
+
|
|
26
|
+
- name: Install e2e dependencies
|
|
27
|
+
run: make dashboard-install-e2e
|
|
28
|
+
|
|
29
|
+
- name: Build frontend artifacts
|
|
30
|
+
run: make frontend-build
|
|
31
|
+
|
|
32
|
+
- name: Run tests
|
|
33
|
+
run: make test-all
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
paths:
|
|
8
|
+
- pyproject.toml
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
id-token: write
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
release:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
environment: pypi
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
with:
|
|
21
|
+
fetch-depth: 0
|
|
22
|
+
|
|
23
|
+
- name: Install uv
|
|
24
|
+
uses: astral-sh/setup-uv@v4
|
|
25
|
+
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
run: uv python install
|
|
28
|
+
|
|
29
|
+
- name: Install bun
|
|
30
|
+
uses: oven-sh/setup-bun@v2
|
|
31
|
+
|
|
32
|
+
- name: Read version
|
|
33
|
+
id: version
|
|
34
|
+
run: |
|
|
35
|
+
python - <<'PY'
|
|
36
|
+
import tomllib
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
|
|
39
|
+
data = tomllib.loads(Path("pyproject.toml").read_text())
|
|
40
|
+
version = data["project"]["version"]
|
|
41
|
+
print(f"version={version}")
|
|
42
|
+
Path("version.txt").write_text(version)
|
|
43
|
+
PY
|
|
44
|
+
echo "version=$(cat version.txt)" >> $GITHUB_OUTPUT
|
|
45
|
+
|
|
46
|
+
- name: Check release
|
|
47
|
+
id: tag
|
|
48
|
+
run: |
|
|
49
|
+
git fetch --tags
|
|
50
|
+
if git rev-parse "v${{ steps.version.outputs.version }}" >/dev/null 2>&1; then
|
|
51
|
+
echo "should_release=false" >> $GITHUB_OUTPUT
|
|
52
|
+
exit 0
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
if git diff -U0 HEAD^ HEAD -- pyproject.toml | grep -E '^[+-]version = "' >/dev/null; then
|
|
56
|
+
echo "should_release=true" >> $GITHUB_OUTPUT
|
|
57
|
+
else
|
|
58
|
+
echo "should_release=false" >> $GITHUB_OUTPUT
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
- name: Install dependencies
|
|
62
|
+
if: steps.tag.outputs.should_release == 'true'
|
|
63
|
+
run: |
|
|
64
|
+
uv sync --all-extras
|
|
65
|
+
cd dashboard-frontend && bun install
|
|
66
|
+
|
|
67
|
+
- name: Install e2e dependencies
|
|
68
|
+
if: steps.tag.outputs.should_release == 'true'
|
|
69
|
+
run: make dashboard-install-e2e
|
|
70
|
+
|
|
71
|
+
- name: Build frontend
|
|
72
|
+
if: steps.tag.outputs.should_release == 'true'
|
|
73
|
+
run: make frontend-build
|
|
74
|
+
|
|
75
|
+
- name: Run tests
|
|
76
|
+
if: steps.tag.outputs.should_release == 'true'
|
|
77
|
+
run: make test-all
|
|
78
|
+
|
|
79
|
+
- name: Build package
|
|
80
|
+
if: steps.tag.outputs.should_release == 'true'
|
|
81
|
+
run: uv build
|
|
82
|
+
|
|
83
|
+
- name: Create tag
|
|
84
|
+
if: steps.tag.outputs.should_release == 'true'
|
|
85
|
+
run: |
|
|
86
|
+
git tag "v${{ steps.version.outputs.version }}"
|
|
87
|
+
git push origin "v${{ steps.version.outputs.version }}"
|
|
88
|
+
|
|
89
|
+
- name: Publish to PyPI
|
|
90
|
+
if: steps.tag.outputs.should_release == 'true'
|
|
91
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
92
|
+
|
|
93
|
+
- name: Create GitHub Release
|
|
94
|
+
if: steps.tag.outputs.should_release == 'true'
|
|
95
|
+
uses: softprops/action-gh-release@v2
|
|
96
|
+
with:
|
|
97
|
+
tag_name: "v${{ steps.version.outputs.version }}"
|
|
98
|
+
files: dist/*
|
|
99
|
+
generate_release_notes: true
|
furu-0.0.1/.gitignore
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib64/
|
|
18
|
+
parts/
|
|
19
|
+
sdist/
|
|
20
|
+
var/
|
|
21
|
+
wheels/
|
|
22
|
+
share/python-wheels/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
MANIFEST
|
|
27
|
+
|
|
28
|
+
# PyInstaller
|
|
29
|
+
# Usually these files are written by a python script from a template
|
|
30
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
31
|
+
*.manifest
|
|
32
|
+
*.spec
|
|
33
|
+
|
|
34
|
+
# Installer logs
|
|
35
|
+
pip-log.txt
|
|
36
|
+
pip-delete-this-directory.txt
|
|
37
|
+
|
|
38
|
+
# Unit test / coverage reports
|
|
39
|
+
htmlcov/
|
|
40
|
+
.tox/
|
|
41
|
+
.nox/
|
|
42
|
+
.coverage
|
|
43
|
+
.coverage.*
|
|
44
|
+
.cache
|
|
45
|
+
nosetests.xml
|
|
46
|
+
coverage.xml
|
|
47
|
+
*.cover
|
|
48
|
+
*.py.cover
|
|
49
|
+
.hypothesis/
|
|
50
|
+
.pytest_cache/
|
|
51
|
+
cover/
|
|
52
|
+
|
|
53
|
+
# Translations
|
|
54
|
+
*.mo
|
|
55
|
+
*.pot
|
|
56
|
+
|
|
57
|
+
# Django stuff:
|
|
58
|
+
*.log
|
|
59
|
+
local_settings.py
|
|
60
|
+
db.sqlite3
|
|
61
|
+
db.sqlite3-journal
|
|
62
|
+
|
|
63
|
+
# Flask stuff:
|
|
64
|
+
instance/
|
|
65
|
+
.webassets-cache
|
|
66
|
+
|
|
67
|
+
# Scrapy stuff:
|
|
68
|
+
.scrapy
|
|
69
|
+
|
|
70
|
+
# Sphinx documentation
|
|
71
|
+
docs/_build/
|
|
72
|
+
|
|
73
|
+
# PyBuilder
|
|
74
|
+
.pybuilder/
|
|
75
|
+
target/
|
|
76
|
+
|
|
77
|
+
# Jupyter Notebook
|
|
78
|
+
.ipynb_checkpoints
|
|
79
|
+
|
|
80
|
+
# IPython
|
|
81
|
+
profile_default/
|
|
82
|
+
ipython_config.py
|
|
83
|
+
|
|
84
|
+
# pyenv
|
|
85
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
86
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
87
|
+
# .python-version
|
|
88
|
+
|
|
89
|
+
# pipenv
|
|
90
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
91
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
92
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
93
|
+
# install all needed dependencies.
|
|
94
|
+
# Pipfile.lock
|
|
95
|
+
|
|
96
|
+
# UV
|
|
97
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
98
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
99
|
+
# commonly ignored for libraries.
|
|
100
|
+
# uv.lock
|
|
101
|
+
|
|
102
|
+
# poetry
|
|
103
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
104
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
105
|
+
# commonly ignored for libraries.
|
|
106
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
107
|
+
# poetry.lock
|
|
108
|
+
# poetry.toml
|
|
109
|
+
|
|
110
|
+
# pdm
|
|
111
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
112
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
113
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
114
|
+
# pdm.lock
|
|
115
|
+
# pdm.toml
|
|
116
|
+
.pdm-python
|
|
117
|
+
.pdm-build/
|
|
118
|
+
|
|
119
|
+
# pixi
|
|
120
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
121
|
+
# pixi.lock
|
|
122
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
123
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
124
|
+
.pixi
|
|
125
|
+
|
|
126
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
127
|
+
__pypackages__/
|
|
128
|
+
|
|
129
|
+
# Celery stuff
|
|
130
|
+
celerybeat-schedule
|
|
131
|
+
celerybeat.pid
|
|
132
|
+
|
|
133
|
+
# Redis
|
|
134
|
+
*.rdb
|
|
135
|
+
*.aof
|
|
136
|
+
*.pid
|
|
137
|
+
|
|
138
|
+
# RabbitMQ
|
|
139
|
+
mnesia/
|
|
140
|
+
rabbitmq/
|
|
141
|
+
rabbitmq-data/
|
|
142
|
+
|
|
143
|
+
# ActiveMQ
|
|
144
|
+
activemq-data/
|
|
145
|
+
|
|
146
|
+
# SageMath parsed files
|
|
147
|
+
*.sage.py
|
|
148
|
+
|
|
149
|
+
# Environments
|
|
150
|
+
.env
|
|
151
|
+
.envrc
|
|
152
|
+
.venv
|
|
153
|
+
env/
|
|
154
|
+
venv/
|
|
155
|
+
ENV/
|
|
156
|
+
env.bak/
|
|
157
|
+
venv.bak/
|
|
158
|
+
|
|
159
|
+
# Spyder project settings
|
|
160
|
+
.spyderproject
|
|
161
|
+
.spyproject
|
|
162
|
+
|
|
163
|
+
# Rope project settings
|
|
164
|
+
.ropeproject
|
|
165
|
+
|
|
166
|
+
# mkdocs documentation
|
|
167
|
+
/site
|
|
168
|
+
|
|
169
|
+
# mypy
|
|
170
|
+
.mypy_cache/
|
|
171
|
+
.dmypy.json
|
|
172
|
+
dmypy.json
|
|
173
|
+
|
|
174
|
+
# Pyre type checker
|
|
175
|
+
.pyre/
|
|
176
|
+
|
|
177
|
+
# pytype static type analyzer
|
|
178
|
+
.pytype/
|
|
179
|
+
|
|
180
|
+
# Cython debug symbols
|
|
181
|
+
cython_debug/
|
|
182
|
+
|
|
183
|
+
# PyCharm
|
|
184
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
185
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
186
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
187
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
188
|
+
# .idea/
|
|
189
|
+
|
|
190
|
+
# Abstra
|
|
191
|
+
# Abstra is an AI-powered process automation framework.
|
|
192
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
193
|
+
# Learn more at https://abstra.io/docs
|
|
194
|
+
.abstra/
|
|
195
|
+
|
|
196
|
+
# Visual Studio Code
|
|
197
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
198
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
199
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
200
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
201
|
+
# .vscode/
|
|
202
|
+
|
|
203
|
+
# Ruff stuff:
|
|
204
|
+
.ruff_cache/
|
|
205
|
+
|
|
206
|
+
# PyPI configuration file
|
|
207
|
+
.pypirc
|
|
208
|
+
|
|
209
|
+
# Marimo
|
|
210
|
+
marimo/_static/
|
|
211
|
+
marimo/_lsp/
|
|
212
|
+
__marimo__/
|
|
213
|
+
|
|
214
|
+
# Streamlit
|
|
215
|
+
.streamlit/secrets.toml
|
|
216
|
+
**/.furu/data
|
|
217
|
+
|
|
218
|
+
dashboard-frontend/node_modules/
|
|
219
|
+
dashboard-frontend/src/routeTree.gen.ts
|
|
220
|
+
dashboard-frontend/src/api/
|
|
221
|
+
|
|
222
|
+
e2e/node_modules/
|
|
223
|
+
e2e/test-results/
|
|
224
|
+
e2e/playwright-report/
|
|
225
|
+
|
|
226
|
+
*.tsbuildinfo
|
|
227
|
+
orval.config.js
|
|
228
|
+
vite.config.js
|
|
229
|
+
|
|
230
|
+
/openapi.json
|
|
231
|
+
data-furu/data
|
furu-0.0.1/AGENTS.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# Repository Guidelines for AI Agents
|
|
2
|
+
|
|
3
|
+
## Critical Rules
|
|
4
|
+
|
|
5
|
+
**Always update `CHANGELOG.md`** for any user-visible change.
|
|
6
|
+
|
|
7
|
+
**DO NOT USE** the following patterns in this codebase:
|
|
8
|
+
- `typing.Optional` - Use `X | None` instead
|
|
9
|
+
- `typing.Any` - Only acceptable when the type is truly unknowable at compile time (e.g., deserializing arbitrary JSON, interacting with untyped third-party libraries). Always prefer protocols, generics, or concrete types.
|
|
10
|
+
- `object` as a type annotation - Use specific types, protocols, or generics instead
|
|
11
|
+
- `dict` without specific value types - Use Pydantic models, dataclasses, or TypedDict instead of `dict[str, Any]` or `dict[str, object]`. Simple dicts like `dict[str, str]` or `dict[str, int]` should also be avoided if it is possible to know exactly what the keys and structure of the data is.
|
|
12
|
+
- `try/except` for error recovery - Prefer happy path and let errors crash; only use try/except for cleanup or resource management
|
|
13
|
+
- Backward compatibility shims or aliases - Do not add backward compatibility code; refactor all usages directly
|
|
14
|
+
|
|
15
|
+
**ALWAYS** use `make` commands rather than running tools directly:
|
|
16
|
+
- `make lint` not `uv run ruff check && uv run ty check`
|
|
17
|
+
- `make test` not `uv run pytest`
|
|
18
|
+
- `make check` for both lint and test
|
|
19
|
+
|
|
20
|
+
**AFTER** making changes, verify your work:
|
|
21
|
+
- For type/import changes: run `make lint`
|
|
22
|
+
- For logic/behavior changes: run `make test` or `make test-all` or `make dashboard-test` or `make dashboard-test-e2e`
|
|
23
|
+
|
|
24
|
+
- The project was renamed from huldra to furu. If I ever refer to huldra, assume I mean furu.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Project Structure
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
src/furu/ # Main package (src layout - import as `furu`)
|
|
32
|
+
core/ # Furu and FuruList classes
|
|
33
|
+
storage/ # StateManager, MetadataManager
|
|
34
|
+
serialization/ # FuruSerializer (+ pydantic support)
|
|
35
|
+
runtime/ # Scoped logging, .env loading, tracebacks
|
|
36
|
+
adapters/ # Integrations (SubmititAdapter)
|
|
37
|
+
dashboard/ # FastAPI dashboard (optional)
|
|
38
|
+
tests/ # pytest tests
|
|
39
|
+
examples/ # Runnable examples
|
|
40
|
+
dashboard-frontend/ # React/TypeScript dashboard frontend
|
|
41
|
+
e2e/ # Playwright end-to-end tests
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Frontend Notes
|
|
47
|
+
|
|
48
|
+
- Use shadcn/ui for frontend components.
|
|
49
|
+
|
|
50
|
+
## Build, Test, and Lint Commands
|
|
51
|
+
|
|
52
|
+
This project uses `uv` for dependency management.
|
|
53
|
+
|
|
54
|
+
### Core Commands (use these)
|
|
55
|
+
|
|
56
|
+
| Command | Description |
|
|
57
|
+
|---------|-------------|
|
|
58
|
+
| `make lint` | Run ruff check + ty type checker |
|
|
59
|
+
| `make test` | Run pytest on `tests/` |
|
|
60
|
+
| `make check` | Run lint + test |
|
|
61
|
+
| `make build` | Build wheel/sdist (runs tests first) |
|
|
62
|
+
| `make clean` | Remove caches and build artifacts |
|
|
63
|
+
|
|
64
|
+
### Running a Single Test
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Run a specific test file
|
|
68
|
+
uv run pytest tests/test_furu_core.py -v
|
|
69
|
+
|
|
70
|
+
# Run a specific test function
|
|
71
|
+
uv run pytest tests/test_furu_core.py::test_exists_reflects_success_state -v
|
|
72
|
+
|
|
73
|
+
# Run tests matching a pattern
|
|
74
|
+
uv run pytest -k "test_load" -v
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Dashboard Commands
|
|
78
|
+
|
|
79
|
+
| Command | Description |
|
|
80
|
+
|---------|-------------|
|
|
81
|
+
| `make dashboard-dev` | Start dev servers (backend + frontend) |
|
|
82
|
+
| `make dashboard-test` | Run dashboard backend tests |
|
|
83
|
+
| `make dashboard-test-e2e` | Run Playwright e2e tests |
|
|
84
|
+
| `make dashboard-test-all` | Run all dashboard tests |
|
|
85
|
+
|
|
86
|
+
### Frontend Commands
|
|
87
|
+
|
|
88
|
+
| Command | Description |
|
|
89
|
+
|---------|-------------|
|
|
90
|
+
| `make frontend-lint` | Run frontend TypeScript type checker |
|
|
91
|
+
| `make frontend-test` | Run frontend unit tests |
|
|
92
|
+
| `make frontend-build` | Build frontend for production |
|
|
93
|
+
| `make frontend-generate` | Generate OpenAPI spec and TypeScript client |
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Code Style
|
|
98
|
+
|
|
99
|
+
### Imports
|
|
100
|
+
|
|
101
|
+
Order imports as: stdlib, third-party, local. Use absolute imports for cross-module references.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
import contextlib
|
|
105
|
+
from pathlib import Path
|
|
106
|
+
import chz
|
|
107
|
+
from ..config import FURU_CONFIG
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Type Annotations
|
|
111
|
+
|
|
112
|
+
- **Required** on all public APIs (functions, methods, class attributes)
|
|
113
|
+
- Use modern syntax: `X | None` not `Optional[X]`
|
|
114
|
+
- Use concrete types, not `Any`
|
|
115
|
+
- Use generics where reasonable: `class Furu[T](ABC):`
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
# Good - specific types
|
|
119
|
+
def process(data: dict[str, str]) -> list[int] | None:
|
|
120
|
+
...
|
|
121
|
+
|
|
122
|
+
# Good - use Pydantic models or dataclasses for structured data
|
|
123
|
+
class UserConfig(BaseModel):
|
|
124
|
+
name: str
|
|
125
|
+
settings: dict[str, str]
|
|
126
|
+
|
|
127
|
+
def load_config(path: Path) -> UserConfig:
|
|
128
|
+
...
|
|
129
|
+
|
|
130
|
+
# Bad - DO NOT USE
|
|
131
|
+
def process(data: Dict[str, Any]) -> Optional[List[int]]:
|
|
132
|
+
...
|
|
133
|
+
|
|
134
|
+
# Bad - DO NOT USE untyped dicts
|
|
135
|
+
def load_config(path: Path) -> dict[str, Any]: # NO - use a model
|
|
136
|
+
...
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Naming Conventions
|
|
140
|
+
|
|
141
|
+
- `snake_case` for functions, variables, module names
|
|
142
|
+
- `PascalCase` for classes
|
|
143
|
+
- `UPPER_SNAKE_CASE` for constants
|
|
144
|
+
- Private/internal names prefixed with `_` (e.g., `_FuruState`, `_iso_now`)
|
|
145
|
+
|
|
146
|
+
### Error Handling
|
|
147
|
+
|
|
148
|
+
**Prefer happy path with early errors over defensive checks.** Don't wrap code in if-statements to handle error cases - let it crash or raise explicitly.
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
# Good - assume happy path, crash if invariant violated
|
|
152
|
+
data = json.loads(path.read_text())
|
|
153
|
+
|
|
154
|
+
# Good - explicit early error instead of nested ifs
|
|
155
|
+
if not condition:
|
|
156
|
+
raise ValueError("condition must be true")
|
|
157
|
+
# proceed with happy path...
|
|
158
|
+
|
|
159
|
+
# Bad - defensive if-checks for non-happy paths
|
|
160
|
+
if path.exists():
|
|
161
|
+
data = json.loads(path.read_text())
|
|
162
|
+
else:
|
|
163
|
+
data = {} # NO - hides bugs, use happy path
|
|
164
|
+
|
|
165
|
+
# Bad - swallowing errors
|
|
166
|
+
try:
|
|
167
|
+
data = json.loads(path.read_text())
|
|
168
|
+
except Exception:
|
|
169
|
+
data = {} # NO - this hides bugs
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Only use try/except for:**
|
|
173
|
+
1. Resource cleanup (use `contextlib.suppress` for ignoring cleanup errors)
|
|
174
|
+
2. Converting exceptions to domain-specific errors
|
|
175
|
+
3. Explicit user-facing error messages
|
|
176
|
+
|
|
177
|
+
### Formatting
|
|
178
|
+
|
|
179
|
+
- 4-space indentation
|
|
180
|
+
- Line length: follow ruff defaults
|
|
181
|
+
- Use trailing commas in multi-line structures
|
|
182
|
+
- Prefer small, focused functions over large ones
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Testing Guidelines
|
|
187
|
+
|
|
188
|
+
- All tests in `tests/` directory using pytest
|
|
189
|
+
- Use `tmp_path` fixture for temporary directories
|
|
190
|
+
- Use `furu_tmp_root` fixture (from `conftest.py`) for isolated Furu config
|
|
191
|
+
- Keep tests deterministic - no writing to project root
|
|
192
|
+
- Test functions named `test_<description>`
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
def test_exists_reflects_success_state(furu_tmp_root) -> None:
|
|
196
|
+
obj = Dummy()
|
|
197
|
+
assert obj.exists() is False
|
|
198
|
+
obj.load_or_create()
|
|
199
|
+
assert obj.exists() is True
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Test Coverage Requirements
|
|
203
|
+
|
|
204
|
+
**ALWAYS write extensive tests when adding new features.** Tests should cover:
|
|
205
|
+
|
|
206
|
+
1. **Happy path** - The feature works as expected with valid inputs
|
|
207
|
+
2. **Edge cases** - Empty inputs, boundary values, None/null values
|
|
208
|
+
3. **Filter combinations** - When adding filters, test each filter individually AND in combination
|
|
209
|
+
4. **Error cases** - Invalid inputs, missing data, malformed requests
|
|
210
|
+
5. **Integration** - Test the full stack (API endpoints, not just internal functions)
|
|
211
|
+
|
|
212
|
+
For dashboard features specifically:
|
|
213
|
+
- Add tests in `tests/dashboard/test_scanner.py` for scanner/filtering logic
|
|
214
|
+
- Add tests in `tests/dashboard/test_api.py` for API endpoint behavior
|
|
215
|
+
- Update `dashboard-frontend/src/api.test.ts` for frontend schema validation
|
|
216
|
+
|
|
217
|
+
Example test structure for a new filter:
|
|
218
|
+
```python
|
|
219
|
+
def test_filter_by_new_field(furu_tmp_root) -> None:
|
|
220
|
+
"""Test filtering by the new field."""
|
|
221
|
+
# Setup: create experiments with different field values
|
|
222
|
+
# Test: filter returns only matching experiments
|
|
223
|
+
# Verify: correct count and correct experiments returned
|
|
224
|
+
|
|
225
|
+
def test_filter_by_new_field_no_match(furu_tmp_root) -> None:
|
|
226
|
+
"""Test filter returns empty when no experiments match."""
|
|
227
|
+
|
|
228
|
+
def test_filter_by_new_field_combined(furu_tmp_root) -> None:
|
|
229
|
+
"""Test new filter works in combination with existing filters."""
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Dashboard Test Performance
|
|
233
|
+
|
|
234
|
+
**Use module-scoped fixtures for read-only tests.** Creating experiments is slow, so:
|
|
235
|
+
|
|
236
|
+
1. **Prefer `populated_furu_root`** (module-scoped) over `temp_furu_root` (function-scoped)
|
|
237
|
+
2. **Extend `_create_populated_experiments()`** in `conftest.py` when you need new test data
|
|
238
|
+
3. **Only use `temp_furu_root`** when tests must mutate state or need isolated data
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
# Good - uses shared fixture, fast
|
|
242
|
+
def test_filter_by_backend(client: TestClient, populated_furu_root: Path) -> None:
|
|
243
|
+
response = client.get("/api/experiments?backend=local")
|
|
244
|
+
assert response.json()["total"] == 3 # Uses pre-created data
|
|
245
|
+
|
|
246
|
+
# Slow - creates experiments per test (avoid unless necessary)
|
|
247
|
+
def test_something(client: TestClient, temp_furu_root: Path) -> None:
|
|
248
|
+
create_experiment_from_furu(...) # Slow!
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Commit Guidelines
|
|
254
|
+
|
|
255
|
+
- Short, imperative subjects (often lowercase)
|
|
256
|
+
- Examples: `fix typing`, `add raw data path to furu config`
|
|
257
|
+
- Keep commits scoped; separate refactors from behavior changes
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Environment & Configuration
|
|
262
|
+
|
|
263
|
+
- Local config from `.env` (gitignored); don't commit secrets
|
|
264
|
+
- Storage defaults to `./data-furu/`; override with `FURU_PATH`
|
|
265
|
+
- Python version: >=3.12
|