falaw 0.0.2__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 (41) hide show
  1. falaw-0.0.2/.claude/skills/falaw/SKILL.md +116 -0
  2. falaw-0.0.2/.gitattributes +1 -0
  3. falaw-0.0.2/.github/workflows/ci.yml +207 -0
  4. falaw-0.0.2/.gitignore +117 -0
  5. falaw-0.0.2/LICENSE +21 -0
  6. falaw-0.0.2/PKG-INFO +126 -0
  7. falaw-0.0.2/README.md +105 -0
  8. falaw-0.0.2/falaw/__init__.py +57 -0
  9. falaw-0.0.2/falaw/__main__.py +54 -0
  10. falaw-0.0.2/falaw/base.py +43 -0
  11. falaw-0.0.2/falaw/bridges/__init__.py +8 -0
  12. falaw-0.0.2/falaw/bridges/mcp.py +16 -0
  13. falaw-0.0.2/falaw/bridges/service.py +16 -0
  14. falaw-0.0.2/falaw/bridges/skill.py +121 -0
  15. falaw-0.0.2/falaw/core.py +72 -0
  16. falaw-0.0.2/falaw/data/models.json +173 -0
  17. falaw-0.0.2/falaw/data/skills/falaw/SKILL.md +116 -0
  18. falaw-0.0.2/falaw/journal.py +132 -0
  19. falaw-0.0.2/falaw/operations/__init__.py +8 -0
  20. falaw-0.0.2/falaw/operations/audio.py +59 -0
  21. falaw-0.0.2/falaw/operations/images.py +69 -0
  22. falaw-0.0.2/falaw/refresh.py +382 -0
  23. falaw-0.0.2/falaw/registry.py +120 -0
  24. falaw-0.0.2/falaw/results.py +136 -0
  25. falaw-0.0.2/falaw/session.py +43 -0
  26. falaw-0.0.2/misc/docs/fal_ai_docs_full.md +92363 -0
  27. falaw-0.0.2/misc/docs/fal_ai_docs_full.pdf +0 -0
  28. falaw-0.0.2/misc/docs/fal_ai_docs_index.md +384 -0
  29. falaw-0.0.2/misc/docs/llms-full.txt +643 -0
  30. falaw-0.0.2/misc/docs/llms.txt +82 -0
  31. falaw-0.0.2/misc/falaw_state/.gitkeep +0 -0
  32. falaw-0.0.2/misc/regenerate_skill.py +30 -0
  33. falaw-0.0.2/misc/scheduled_refresh.py +48 -0
  34. falaw-0.0.2/pyproject.toml +162 -0
  35. falaw-0.0.2/tests/__init__.py +0 -0
  36. falaw-0.0.2/tests/test_journal.py +20 -0
  37. falaw-0.0.2/tests/test_operations.py +63 -0
  38. falaw-0.0.2/tests/test_refresh.py +132 -0
  39. falaw-0.0.2/tests/test_registry.py +34 -0
  40. falaw-0.0.2/tests/test_results.py +36 -0
  41. falaw-0.0.2/tests/test_skill_bridge.py +11 -0
@@ -0,0 +1,116 @@
1
+ ---
2
+ name: falaw
3
+ description: >-
4
+ Generate and manage AI media (images, video, audio) via fal.ai. Use this
5
+ skill whenever the user wants to generate, edit, upscale, or compose media,
6
+ or asks about fal.ai or fal-client. The skill exposes Python tools that
7
+ wrap fal-client with smart model selection, result handling, and a journal
8
+ for self-improvement across sessions.
9
+ ---
10
+
11
+ # falaw
12
+
13
+ Use the `falaw` Python package as your primary interface to fal.ai.
14
+
15
+ ## Read the journal first
16
+
17
+ Before doing anything novel, glance at recent journal entries --- past
18
+ sessions may have left notes, gotchas, or suggestions that save you time:
19
+
20
+ ```python
21
+ from falaw import journal
22
+ for e in journal.recent(20):
23
+ print(e.kind, '-', e.text[:120])
24
+ ```
25
+
26
+ ## Leave a journal entry when something surprises you
27
+
28
+ Whenever a model behaved oddly, an error was confusing, or you had to work
29
+ around something, leave a note. This is how the next session learns:
30
+
31
+ ```python
32
+ from falaw import journal
33
+ journal.issue("FLUX dev returned NSFW=True for a benign prompt",
34
+ suggestion="Document an example that triggers it",
35
+ tags=("flux", "safety"))
36
+ journal.improvement("Add an `upscale_image` tool that wraps clarity-upscaler",
37
+ tags=("backlog",))
38
+ journal.note("schnell quality=fast returns 1024x1024 by default")
39
+ ```
40
+
41
+ ## Pick a model without memorizing IDs
42
+
43
+ ```python
44
+ from falaw import list_models, pick_model
45
+ [m.id for m in list_models(category='video')]
46
+ pick_model(category='image', quality_tier='ultra').id
47
+ ```
48
+
49
+ ## Tools
50
+
51
+ The functions below are also registered tools; bridges (MCP server, HTTP
52
+ service, UI) derive their surfaces from the same registry.
53
+
54
+
55
+ ### `falaw.generate_image`
56
+
57
+ Generate an image from a text prompt. Picks a sensible FLUX model based on the requested quality tier ('fast', 'balanced', 'high', 'ultra'). Returns a falaw.Result whose .first asset has a URL you can .download(to=...).
58
+
59
+ Examples:
60
+ - `falaw.generate_image(**{'prompt': 'A red panda eating bamboo', 'quality': 'fast'})`
61
+ - `falaw.generate_image(**{'prompt': 'Cinematic portrait, 35mm film', 'quality': 'ultra'})`
62
+
63
+
64
+ ### `falaw.text_to_speech`
65
+
66
+ Synthesize speech from text. Picks a TTS model by quality tier; pass `voice` and `extra` for model-specific knobs. Returns a falaw.Result whose .first asset is the audio URL.
67
+
68
+ Examples:
69
+ - `falaw.text_to_speech(**{'text': 'Hello world', 'quality': 'balanced'})`
70
+ - `falaw.text_to_speech(**{'text': 'Bonjour le monde', 'quality': 'high', 'voice': 'fr-FR-female-1'})`
71
+
72
+
73
+ ### `falaw.refresh_llms`
74
+
75
+ Refresh `llms.txt` and `llms-full.txt` from fal.ai using conditional GETs (ETag-based). Cheap: returns immediately if nothing changed. On change, snapshots the previous version and journals the diff. Returns a summary dict like {'llms': {'changed': False}, ...}.
76
+
77
+
78
+ ### `falaw.refresh_full_docs`
79
+
80
+ Re-crawl every per-page .md endpoint listed in `fal_ai_docs_index.md` with conditional GETs, then reassemble `fal_ai_docs_full.md`. Heavy. Gated on `is_stale(llms-full)` by default --- pass `force=True` to skip the gate. Pages that 304 are skipped; only changed pages re-download. Logs a single journal entry summarizing the run.
81
+
82
+
83
+ ## Models known to falaw
84
+
85
+ The model registry lives at `falaw/data/models.json`. Refresh it from
86
+ `misc/docs/fal_ai_docs_full.md` when fal ships new models. Quick view:
87
+
88
+ ```
89
+ avatar balanced fal-ai/ai-avatar
90
+ background_removal high fal-ai/birefnet/v2
91
+ image balanced fal-ai/flux/dev
92
+ image fast fal-ai/flux/schnell
93
+ image high fal-ai/flux-pro/v1.1
94
+ image ultra fal-ai/flux-pro/v1.1-ultra
95
+ image_edit ultra fal-ai/flux-pro/kontext/max
96
+ lipsync high fal-ai/sync-lipsync/v2
97
+ llm balanced fal-ai/any-llm
98
+ music balanced fal-ai/diffrhythm
99
+ music high fal-ai/lyria2
100
+ tts balanced fal-ai/playai/tts/v3
101
+ tts high fal-ai/minimax/speech-02-hd
102
+ tts high fal-ai/elevenlabs/tts/multilingual-v2
103
+ upscale high fal-ai/clarity-upscaler
104
+ video balanced fal-ai/minimax/hailuo-02/pro/image-to-video
105
+ video high fal-ai/kling-video/v2.1/master/image-to-video
106
+ video ultra fal-ai/veo3
107
+ voice_clone high fal-ai/minimax/voice-clone
108
+ ```
109
+
110
+ ## When you can't find what you need
111
+
112
+ * Check `falaw/misc/docs/llms-full.txt` for a structured fal.ai overview.
113
+ * Check `falaw/misc/docs/fal_ai_docs_full.md` for the full corpus (~3MB).
114
+ * Drop into `falaw.call_fal(application, arguments)` for any model not
115
+ yet wrapped --- this is the escape hatch. Then leave a `journal.improvement`
116
+ asking for a proper tool wrapper.
@@ -0,0 +1 @@
1
+ *.ipynb linguist-documentation
@@ -0,0 +1,207 @@
1
+ name: Continuous Integration (uv)
2
+ on: [push, pull_request]
3
+
4
+ # Note: Environment variables (PROJECT_NAME and vars from [tool.wads.ci.env])
5
+ # are set by the read-ci-config action in the setup job and made available
6
+ # to all subsequent jobs via GITHUB_ENV
7
+
8
+ jobs:
9
+ # First job: Read configuration from pyproject.toml
10
+ setup:
11
+ name: Read Configuration
12
+ runs-on: ubuntu-latest
13
+ outputs:
14
+ project-name: ${{ steps.config.outputs.project-name }}
15
+ python-versions: ${{ steps.config.outputs.python-versions }}
16
+ pytest-args: ${{ steps.config.outputs.pytest-args }}
17
+ coverage-enabled: ${{ steps.config.outputs.coverage-enabled }}
18
+ exclude-paths: ${{ steps.config.outputs.exclude-paths }}
19
+ test-on-windows: ${{ steps.config.outputs.test-on-windows }}
20
+ build-sdist: ${{ steps.config.outputs.build-sdist }}
21
+ build-wheel: ${{ steps.config.outputs.build-wheel }}
22
+ metrics-enabled: ${{ steps.config.outputs.metrics-enabled }}
23
+ metrics-config-path: ${{ steps.config.outputs.metrics-config-path }}
24
+ metrics-storage-branch: ${{ steps.config.outputs.metrics-storage-branch }}
25
+ metrics-python-version: ${{ steps.config.outputs.metrics-python-version }}
26
+ metrics-force-run: ${{ steps.config.outputs.metrics-force-run }}
27
+
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+
31
+ - name: Set up uv
32
+ uses: astral-sh/setup-uv@v5
33
+
34
+ - name: Set up Python
35
+ run: uv python install 3.11
36
+
37
+ - name: Read CI Config
38
+ id: config
39
+ uses: i2mint/wads/actions/read-ci-config@master
40
+ with:
41
+ pyproject-path: .
42
+
43
+ # Second job: Validation using the config
44
+ validation:
45
+ name: Validation
46
+ if: "!contains(github.event.head_commit.message, '[skip ci]')"
47
+ needs: setup
48
+ runs-on: ubuntu-latest
49
+ strategy:
50
+ matrix:
51
+ python-version: ${{ fromJson(needs.setup.outputs.python-versions) }}
52
+
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+
56
+ - name: Set up uv
57
+ uses: astral-sh/setup-uv@v5
58
+ with:
59
+ enable-cache: true
60
+
61
+ - name: Set up Python ${{ matrix.python-version }}
62
+ uses: i2mint/wads/actions/setup-python-uv@master
63
+ with:
64
+ python-version: ${{ matrix.python-version }}
65
+
66
+ - name: Install System Dependencies
67
+ uses: i2mint/wads/actions/install-system-deps@master
68
+ with:
69
+ pyproject-path: .
70
+
71
+ - name: Install Dependencies
72
+ uses: i2mint/wads/actions/install-deps-uv@master
73
+
74
+ - name: Format Source Code
75
+ run: uvx ruff format .
76
+
77
+ - name: Lint Validation
78
+ run: uvx ruff check --output-format=github ${{ needs.setup.outputs.project-name }}
79
+
80
+ - name: Run Tests
81
+ uses: i2mint/wads/actions/run-tests-uv@master
82
+ with:
83
+ root-dir: ${{ needs.setup.outputs.project-name }}
84
+ pytest-args: ${{ needs.setup.outputs.pytest-args }}
85
+ exclude-paths: ${{ needs.setup.outputs.exclude-paths }}
86
+ coverage: ${{ needs.setup.outputs.coverage-enabled }}
87
+
88
+ - name: Track Code Metrics
89
+ if: needs.setup.outputs.metrics-enabled == 'true'
90
+ uses: i2mint/umpyre/actions/track-metrics@master
91
+ continue-on-error: true
92
+ with:
93
+ github-token: ${{ secrets.GITHUB_TOKEN }}
94
+ config-path: ${{ needs.setup.outputs.metrics-config-path }}
95
+ storage-branch: ${{ needs.setup.outputs.metrics-storage-branch }}
96
+ python-version: ${{ needs.setup.outputs.metrics-python-version }}
97
+ force-run: ${{ needs.setup.outputs.metrics-force-run }}
98
+
99
+ # Optional Windows testing (if enabled in config)
100
+ windows-validation:
101
+ name: Windows Tests
102
+ if: "!contains(github.event.head_commit.message, '[skip ci]') && needs.setup.outputs.test-on-windows == 'true'"
103
+ needs: setup
104
+ runs-on: windows-latest
105
+ continue-on-error: true
106
+
107
+ steps:
108
+ - uses: actions/checkout@v4
109
+
110
+ - name: Set up uv
111
+ uses: astral-sh/setup-uv@v5
112
+ with:
113
+ enable-cache: true
114
+
115
+ - name: Set up Python
116
+ uses: i2mint/wads/actions/setup-python-uv@master
117
+ with:
118
+ python-version: ${{ fromJson(needs.setup.outputs.python-versions)[0] }}
119
+
120
+ - name: Install System Dependencies
121
+ uses: i2mint/wads/actions/install-system-deps@master
122
+ with:
123
+ pyproject-path: .
124
+
125
+ - name: Install Dependencies
126
+ uses: i2mint/wads/actions/install-deps-uv@master
127
+
128
+ - name: Run Tests
129
+ uses: i2mint/wads/actions/run-tests-uv@master
130
+ with:
131
+ root-dir: ${{ needs.setup.outputs.project-name }}
132
+
133
+ # Publishing job
134
+ publish:
135
+ name: Publish
136
+ permissions:
137
+ contents: write
138
+ if: "!contains(github.event.head_commit.message, '[skip ci]') && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main')"
139
+ needs: [setup, validation]
140
+ runs-on: ubuntu-latest
141
+
142
+ steps:
143
+ - uses: actions/checkout@v4
144
+ with:
145
+ fetch-depth: 0
146
+ token: ${{ secrets.GITHUB_TOKEN }}
147
+
148
+ - name: Set up uv
149
+ uses: astral-sh/setup-uv@v5
150
+
151
+ - name: Set up Python
152
+ uses: i2mint/wads/actions/setup-python-uv@master
153
+ with:
154
+ python-version: ${{ fromJson(needs.setup.outputs.python-versions)[0] }}
155
+ create-venv: "false"
156
+
157
+ - name: Format Source Code
158
+ run: uvx ruff format .
159
+
160
+ - name: Update Version Number
161
+ id: version
162
+ uses: i2mint/isee/actions/bump-version-number@master
163
+
164
+ - name: Build Distribution
165
+ uses: i2mint/wads/actions/build-dist-uv@master
166
+ with:
167
+ sdist: ${{ needs.setup.outputs.build-sdist }}
168
+ wheel: ${{ needs.setup.outputs.build-wheel }}
169
+
170
+ - name: Publish to PyPI
171
+ uses: i2mint/wads/actions/pypi-publish-uv@master
172
+ with:
173
+ pypi-token: ${{ secrets.PYPI_PASSWORD }}
174
+
175
+ - name: Force SSH for git remote
176
+ run: git remote set-url origin git@github.com:${{ github.repository }}.git
177
+
178
+ - name: Commit Changes
179
+ uses: i2mint/wads/actions/git-commit@master
180
+ with:
181
+ commit-message: "**CI** Formatted code + Updated version to ${{ env.VERSION }} [skip ci]"
182
+ ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
183
+ push: true
184
+
185
+ - name: Tag Repository
186
+ uses: i2mint/wads/actions/git-tag@master
187
+ with:
188
+ tag: ${{ env.VERSION }}
189
+ message: "Release version ${{ env.VERSION }}"
190
+ push: true
191
+
192
+ # Optional GitHub Pages
193
+ github-pages:
194
+ name: Publish GitHub Pages
195
+ permissions:
196
+ contents: write
197
+ pages: write
198
+ id-token: write
199
+ if: "!contains(github.event.head_commit.message, '[skip ci]') && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)"
200
+ needs: publish
201
+ runs-on: ubuntu-latest
202
+
203
+ steps:
204
+ - uses: i2mint/epythet/actions/publish-github-pages@master
205
+ with:
206
+ github-token: ${{ secrets.GITHUB_TOKEN }}
207
+ ignore: "tests/,scrap/,examples/"
falaw-0.0.2/.gitignore ADDED
@@ -0,0 +1,117 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+
7
+ .DS_Store
8
+ # C extensions
9
+ *.so
10
+
11
+ # TLS certificates
12
+ ## Ignore all PEM files anywhere
13
+ *.pem
14
+ ## Also ignore any certs directory
15
+ certs/
16
+
17
+ # Distribution / packaging
18
+ .Python
19
+ build/
20
+ develop-eggs/
21
+ dist/
22
+ downloads/
23
+ eggs/
24
+ .eggs/
25
+ lib/
26
+ lib64/
27
+ parts/
28
+ sdist/
29
+ var/
30
+ wheels/
31
+ *.egg-info/
32
+ .installed.cfg
33
+ *.egg
34
+ MANIFEST
35
+ _build
36
+
37
+ # PyInstaller
38
+ # Usually these files are written by a python script from a template
39
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
40
+ *.manifest
41
+ *.spec
42
+
43
+ # Installer logs
44
+ pip-log.txt
45
+ pip-delete-this-directory.txt
46
+
47
+ # Unit test / coverage reports
48
+ htmlcov/
49
+ .tox/
50
+ .coverage
51
+ .coverage.*
52
+ .cache
53
+ nosetests.xml
54
+ coverage.xml
55
+ *.cover
56
+ .hypothesis/
57
+ .pytest_cache/
58
+
59
+ # Translations
60
+ *.mo
61
+ *.pot
62
+
63
+ # Django stuff:
64
+ *.log
65
+ local_settings.py
66
+ db.sqlite3
67
+
68
+ # Flask stuff:
69
+ instance/
70
+ .webassets-cache
71
+
72
+ # Scrapy stuff:
73
+ .scrapy
74
+
75
+ # Sphinx documentation
76
+ docs/_build/
77
+ docs/*
78
+
79
+ # PyBuilder
80
+ target/
81
+
82
+ # Jupyter Notebook
83
+ .ipynb_checkpoints
84
+
85
+ # pyenv
86
+ .python-version
87
+
88
+ # celery beat schedule file
89
+ celerybeat-schedule
90
+
91
+ # SageMath parsed files
92
+ *.sage.py
93
+
94
+ # Environments
95
+ .env
96
+ .venv
97
+ env/
98
+ venv/
99
+ ENV/
100
+ env.bak/
101
+ venv.bak/
102
+
103
+ # Spyder project settings
104
+ .spyderproject
105
+ .spyproject
106
+
107
+ # Rope project settings
108
+ .ropeproject
109
+
110
+ # mkdocs documentation
111
+ /site
112
+
113
+ # mypy
114
+ .mypy_cache/
115
+
116
+ # PyCharm
117
+ .idea
falaw-0.0.2/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Thor Whalen
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.
falaw-0.0.2/PKG-INFO ADDED
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.4
2
+ Name: falaw
3
+ Version: 0.0.2
4
+ Summary: Agent-friendly Python facade over fal.ai for generating and managing AI media (images, video, audio).
5
+ Project-URL: Homepage, https://github.com/thorwhalen/falaw
6
+ Project-URL: Repository, https://github.com/thorwhalen/falaw
7
+ Project-URL: Documentation, https://thorwhalen.github.io/falaw
8
+ Author: Thor Whalen
9
+ License: mit
10
+ License-File: LICENSE
11
+ Requires-Python: >=3.10
12
+ Requires-Dist: fal-client>=0.5
13
+ Provides-Extra: dev
14
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
15
+ Requires-Dist: pytest>=7.0; extra == 'dev'
16
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
17
+ Provides-Extra: docs
18
+ Requires-Dist: sphinx-rtd-theme>=1.0; extra == 'docs'
19
+ Requires-Dist: sphinx>=6.0; extra == 'docs'
20
+ Description-Content-Type: text/markdown
21
+
22
+ # falaw
23
+
24
+ Agent-friendly Python facade over fal.ai for generating and managing AI media (images, video, audio).
25
+
26
+ ```python
27
+ from falaw import generate_image, list_models, journal
28
+
29
+ r = generate_image("a tiger eye, macro, 35mm", quality="fast")
30
+ r.first.download(to="./tiger.png")
31
+
32
+ [m.id for m in list_models(category="video")]
33
+ journal.note("schnell at quality='fast' defaults to 1024x1024")
34
+ ```
35
+
36
+ ## Why
37
+
38
+ `fal-client` already gives you 100+ models behind a uniform call. What
39
+ agents (and humans) still struggle with is *which* model to use, *what*
40
+ parameters it takes, and *what to do with* the URL it returns. `falaw`
41
+ adds:
42
+
43
+ - Task-level verbs (`generate_image`, `text_to_speech`, ...) with smart model selection by quality tier.
44
+ - A queryable model registry --- no more grepping docs for IDs.
45
+ - `Result` / `Asset` objects that download, name, and organize outputs.
46
+ - A journal so each session leaves notes for the next one.
47
+ - A Claude skill, plus stub bridges for MCP and HTTP services --- all derived from the same tool registry.
48
+
49
+ ## Install
50
+
51
+ ```bash
52
+ pip install -e .
53
+ export FAL_KEY="your-fal-api-key"
54
+ ```
55
+
56
+ ## Core surface
57
+
58
+ | Function | Purpose |
59
+ | --- | --- |
60
+ | `generate_image(prompt, *, quality, image_size, model_id, extra)` | Text-to-image, picks FLUX by quality tier. |
61
+ | `text_to_speech(text, *, quality, voice, model_id, extra)` | TTS, picks a voice model by tier. |
62
+ | `list_models(*, category, quality_tier)` | Browse the catalog. |
63
+ | `pick_model(*, category, quality_tier)` | Pick a sensible default. |
64
+ | `call_fal(application, arguments)` | Escape hatch to any fal model. Auto-journals on error. |
65
+ | `journal.note / issue / improvement(...)` | Leave a trace for future sessions. |
66
+ | `Session(output_dir=...)` | Optional stateful controller. |
67
+
68
+ ## Architecture
69
+
70
+ Single source of truth: a `ToolSpec` dataclass per tool. From it we derive every external surface:
71
+
72
+ ```
73
+ falaw.registry ──► bridges/skill.py ──► .claude/skills/falaw/SKILL.md
74
+ ──► bridges/mcp.py ──► MCP server (planned)
75
+ ──► bridges/service.py ──► qh HTTP service (planned)
76
+ ──► (UI) (planned)
77
+ ```
78
+
79
+ Adding a new surface is a new bridge module, never a re-implementation of the operations.
80
+
81
+ ## Self-improvement loop
82
+
83
+ Every session can read and write the agent journal at `~/.config/falaw/journal/`. The Claude skill instructs Claude to:
84
+
85
+ 1. Read recent entries before novel work.
86
+ 2. Write a note / issue / improvement when something surprises it.
87
+
88
+ `call_fal` auto-journals failures with the application id and arguments, so the next session recognizes the trap.
89
+
90
+ ## Layout
91
+
92
+ ```
93
+ falaw/
94
+ base.py ToolSpec, ModelRecord
95
+ core.py call_fal: subscribe + auto-journal
96
+ registry.py register_tool, list/get/pick model
97
+ results.py Asset, Result, parse_response
98
+ session.py Session
99
+ journal.py file-backed journal
100
+ operations/
101
+ images.py generate_image
102
+ audio.py text_to_speech
103
+ bridges/
104
+ skill.py render Claude SKILL.md from registry
105
+ mcp.py (stub)
106
+ service.py (stub)
107
+ data/
108
+ models.json seed catalog
109
+ skills/falaw/ generated skill files (shipped with package)
110
+ misc/
111
+ docs/ aggregated fal.ai docs (3MB md, llms.txt, llms-full.txt)
112
+ regenerate_skill.py
113
+ tests/
114
+ ```
115
+
116
+ ## Regenerate the skill after adding a tool
117
+
118
+ ```bash
119
+ python misc/regenerate_skill.py
120
+ ```
121
+
122
+ Writes `falaw/data/skills/falaw/SKILL.md` and `.claude/skills/falaw/SKILL.md`.
123
+
124
+ ## Status
125
+
126
+ v0 --- functional core, real Claude skill, stubs for MCP and HTTP service. The bridges share the same registry, so filling in the stubs is additive.
falaw-0.0.2/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # falaw
2
+
3
+ Agent-friendly Python facade over fal.ai for generating and managing AI media (images, video, audio).
4
+
5
+ ```python
6
+ from falaw import generate_image, list_models, journal
7
+
8
+ r = generate_image("a tiger eye, macro, 35mm", quality="fast")
9
+ r.first.download(to="./tiger.png")
10
+
11
+ [m.id for m in list_models(category="video")]
12
+ journal.note("schnell at quality='fast' defaults to 1024x1024")
13
+ ```
14
+
15
+ ## Why
16
+
17
+ `fal-client` already gives you 100+ models behind a uniform call. What
18
+ agents (and humans) still struggle with is *which* model to use, *what*
19
+ parameters it takes, and *what to do with* the URL it returns. `falaw`
20
+ adds:
21
+
22
+ - Task-level verbs (`generate_image`, `text_to_speech`, ...) with smart model selection by quality tier.
23
+ - A queryable model registry --- no more grepping docs for IDs.
24
+ - `Result` / `Asset` objects that download, name, and organize outputs.
25
+ - A journal so each session leaves notes for the next one.
26
+ - A Claude skill, plus stub bridges for MCP and HTTP services --- all derived from the same tool registry.
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ pip install -e .
32
+ export FAL_KEY="your-fal-api-key"
33
+ ```
34
+
35
+ ## Core surface
36
+
37
+ | Function | Purpose |
38
+ | --- | --- |
39
+ | `generate_image(prompt, *, quality, image_size, model_id, extra)` | Text-to-image, picks FLUX by quality tier. |
40
+ | `text_to_speech(text, *, quality, voice, model_id, extra)` | TTS, picks a voice model by tier. |
41
+ | `list_models(*, category, quality_tier)` | Browse the catalog. |
42
+ | `pick_model(*, category, quality_tier)` | Pick a sensible default. |
43
+ | `call_fal(application, arguments)` | Escape hatch to any fal model. Auto-journals on error. |
44
+ | `journal.note / issue / improvement(...)` | Leave a trace for future sessions. |
45
+ | `Session(output_dir=...)` | Optional stateful controller. |
46
+
47
+ ## Architecture
48
+
49
+ Single source of truth: a `ToolSpec` dataclass per tool. From it we derive every external surface:
50
+
51
+ ```
52
+ falaw.registry ──► bridges/skill.py ──► .claude/skills/falaw/SKILL.md
53
+ ──► bridges/mcp.py ──► MCP server (planned)
54
+ ──► bridges/service.py ──► qh HTTP service (planned)
55
+ ──► (UI) (planned)
56
+ ```
57
+
58
+ Adding a new surface is a new bridge module, never a re-implementation of the operations.
59
+
60
+ ## Self-improvement loop
61
+
62
+ Every session can read and write the agent journal at `~/.config/falaw/journal/`. The Claude skill instructs Claude to:
63
+
64
+ 1. Read recent entries before novel work.
65
+ 2. Write a note / issue / improvement when something surprises it.
66
+
67
+ `call_fal` auto-journals failures with the application id and arguments, so the next session recognizes the trap.
68
+
69
+ ## Layout
70
+
71
+ ```
72
+ falaw/
73
+ base.py ToolSpec, ModelRecord
74
+ core.py call_fal: subscribe + auto-journal
75
+ registry.py register_tool, list/get/pick model
76
+ results.py Asset, Result, parse_response
77
+ session.py Session
78
+ journal.py file-backed journal
79
+ operations/
80
+ images.py generate_image
81
+ audio.py text_to_speech
82
+ bridges/
83
+ skill.py render Claude SKILL.md from registry
84
+ mcp.py (stub)
85
+ service.py (stub)
86
+ data/
87
+ models.json seed catalog
88
+ skills/falaw/ generated skill files (shipped with package)
89
+ misc/
90
+ docs/ aggregated fal.ai docs (3MB md, llms.txt, llms-full.txt)
91
+ regenerate_skill.py
92
+ tests/
93
+ ```
94
+
95
+ ## Regenerate the skill after adding a tool
96
+
97
+ ```bash
98
+ python misc/regenerate_skill.py
99
+ ```
100
+
101
+ Writes `falaw/data/skills/falaw/SKILL.md` and `.claude/skills/falaw/SKILL.md`.
102
+
103
+ ## Status
104
+
105
+ v0 --- functional core, real Claude skill, stubs for MCP and HTTP service. The bridges share the same registry, so filling in the stubs is additive.