brandly-cli 0.3.2__tar.gz → 0.3.4__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.
- brandly_cli-0.3.4/.github/workflows/ci.yml +71 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/.github/workflows/release.yml +10 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/.gitignore +2 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/.pre-commit-config.yaml +1 -1
- brandly_cli-0.3.4/LICENSE +21 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/Makefile +20 -7
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/PKG-INFO +2 -1
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/pyproject.toml +1 -1
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/__about__.py +1 -1
- brandly_cli-0.3.4/src/brandly_cli/agent_tools.py +312 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/agnes_client.py +244 -2
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/audio_client.py +23 -2
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/autodirector.py +1 -1
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/beat_sync.py +1 -1
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/cli.py +278 -42
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/constants.py +63 -16
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/dubbing.py +86 -11
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/export_platforms.py +1 -1
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/minimax_client.py +134 -52
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/sharing.py +1 -1
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/stitch.py +1 -1
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/thumbnails.py +3 -3
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/utils.py +1 -1
- brandly_cli-0.3.4/tests/test_agent_tools.py +267 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_cli.py +19 -0
- brandly_cli-0.3.4/tests/test_minimax.py +261 -0
- brandly_cli-0.3.2/$null +0 -2
- brandly_cli-0.3.2/.github/workflows/ci.yml +0 -37
- brandly_cli-0.3.2/byteplus-ark-cli-1.0.24.tgz +0 -0
- brandly_cli-0.3.2/generate_svd.py +0 -101
- brandly_cli-0.3.2/package/LICENSE +0 -201
- brandly_cli-0.3.2/package/README.md +0 -47
- brandly_cli-0.3.2/package/manifest.json +0 -72
- brandly_cli-0.3.2/package/package.json +0 -48
- brandly_cli-0.3.2/package/scripts/postinstall.js +0 -545
- brandly_cli-0.3.2/package/scripts/run.js +0 -44
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/.env.example +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/README.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/assets/banner.png +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/dev-notes/AUDIT.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/dev-notes/DEVELOPMENT.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/dev-notes/GOAL.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/dev-notes/IMPLEMENTATION.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/dev-notes/PROGRESS.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/dev-notes/SECURITY.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/dev-notes/SPEC.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/scripts/install-hooks.sh +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/README.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-animal-sheet/SKILL.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-animal-sheet/references/habitats.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-animal-sheet/references/prompt-variants.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-animal-sheet/references/species-guide.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-character-sheet/SKILL.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-character-sheet/references/common-types.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-character-sheet/references/consistency-checklist.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-character-sheet/references/prompt-variants.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-consistency/SKILL.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-consistency/references/anchoring-system.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-consistency/references/drift-checklist.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-consistency/references/lock-strength.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-location-sheet/SKILL.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-location-sheet/references/lighting-setups.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-location-sheet/references/location-types.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-location-sheet/references/prompt-variants.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-mecha-sheet/SKILL.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-mecha-sheet/references/mecha-types.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-mecha-sheet/references/prompt-variants.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-mecha-sheet/references/technical-reference.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-object-sheet/SKILL.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-object-sheet/references/photography-setups.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-object-sheet/references/product-categories.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-object-sheet/references/prompt-variants.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-plant-sheet/SKILL.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-plant-sheet/references/plant-species.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-plant-sheet/references/prompt-variants.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-plant-sheet/references/seasonal-reference.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-production-bible/SKILL.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-production-bible/references/asset-map.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-production-bible/references/bible-template.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-production-bible/references/example-bible.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-storyboard/SKILL.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-storyboard/references/examples.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-storyboard/references/pacing-guide.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-storyboard/references/shot-formats.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-vehicle-sheet/SKILL.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-vehicle-sheet/references/prompt-variants.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-vehicle-sheet/references/shot-angles.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-vehicle-sheet/references/vehicle-types.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-video-generation/SKILL.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-video-generation/references/camera-control.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-video-generation/references/character-consistency.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-video-generation/references/lighting-presets.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-video-generation/references/troubleshooting.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/skills/brandly-video-generation/references/video-styles.md +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/__init__.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/__main__.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/analyzer.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/ark_client.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/captions.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/cost_tracker.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/director.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/edit.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/memory.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/project_manager.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/style_presets.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/sync.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/templates.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/trends.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/types.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/video_prompts.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/src/brandly_cli/webhook.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/__init__.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/_check.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_agnes_client.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_analyzer.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_ark_client.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_autodirector.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_beat_sync.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_brandly_cli.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_dubbing.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_edit.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_export_platforms.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_reference.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_sharing.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_stitch.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_templates.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_thumbnails.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_trends.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_video_prompts.py +0 -0
- {brandly_cli-0.3.2 → brandly_cli-0.3.4}/tests/test_webhook.py +0 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, synchronize, reopened]
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
# Cancel superseded runs on the same ref (for PRs); keep main runs to
|
|
10
|
+
# avoid clobbering a long build when a new commit lands mid-run.
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
quality:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
strategy:
|
|
19
|
+
fail-fast: false
|
|
20
|
+
matrix:
|
|
21
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Install FFmpeg
|
|
26
|
+
run: sudo apt-get update && sudo apt-get install -y ffmpeg
|
|
27
|
+
|
|
28
|
+
- uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: ${{ matrix.python-version }}
|
|
31
|
+
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: |
|
|
34
|
+
python -m pip install --upgrade pip
|
|
35
|
+
pip install -e ".[dev]" build twine
|
|
36
|
+
|
|
37
|
+
- name: Lint
|
|
38
|
+
run: ruff check src/ tests/
|
|
39
|
+
|
|
40
|
+
- name: Type check
|
|
41
|
+
run: mypy src/
|
|
42
|
+
|
|
43
|
+
- name: Syntax check
|
|
44
|
+
run: python -m py_compile src/brandly_cli/*.py
|
|
45
|
+
|
|
46
|
+
- name: Run tests
|
|
47
|
+
run: |
|
|
48
|
+
python -m pytest tests/ -v --tb=short \
|
|
49
|
+
--cov=brandly_cli --cov-report=xml --cov-report=term-missing
|
|
50
|
+
|
|
51
|
+
- name: Build package
|
|
52
|
+
run: python -m build
|
|
53
|
+
|
|
54
|
+
- name: Check build
|
|
55
|
+
run: python -m twine check dist/*
|
|
56
|
+
|
|
57
|
+
- name: Upload dist
|
|
58
|
+
if: ${{ matrix.python-version == '3.12' }}
|
|
59
|
+
uses: actions/upload-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
name: dist
|
|
62
|
+
path: dist/
|
|
63
|
+
if-no-files-found: error
|
|
64
|
+
|
|
65
|
+
- name: Upload coverage
|
|
66
|
+
if: ${{ matrix.python-version == '3.12' }}
|
|
67
|
+
uses: actions/upload-artifact@v4
|
|
68
|
+
with:
|
|
69
|
+
name: coverage
|
|
70
|
+
path: coverage.xml
|
|
71
|
+
if-no-files-found: warn
|
|
@@ -29,5 +29,15 @@ jobs:
|
|
|
29
29
|
- name: Build package
|
|
30
30
|
run: python -m build
|
|
31
31
|
|
|
32
|
+
- name: Check build
|
|
33
|
+
run: python -m twine check dist/*
|
|
34
|
+
|
|
35
|
+
- name: Upload dist
|
|
36
|
+
uses: actions/upload-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: dist-${{ github.event.release.tag_name }}
|
|
39
|
+
path: dist/
|
|
40
|
+
if-no-files-found: error
|
|
41
|
+
|
|
32
42
|
- name: Release to PyPI
|
|
33
43
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Dream Pixels Forge
|
|
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.
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# Brandly CLI — Makefile
|
|
2
2
|
|
|
3
3
|
PYTHON := python3
|
|
4
|
-
PNPM := pnpm
|
|
5
4
|
PIP := $(PYTHON) -m pip
|
|
6
5
|
PYTEST := $(PYTHON) -m pytest
|
|
7
6
|
RUFF := $(PYTHON) -m ruff
|
|
7
|
+
MYPY := $(PYTHON) -m mypy
|
|
8
8
|
ROOT ?= .
|
|
9
9
|
|
|
10
|
-
.PHONY: help install dev test lint type-check
|
|
11
|
-
pre-commit ci e2e upgrade sync
|
|
10
|
+
.PHONY: help install dev test cov lint lint-fix check-syntax type-check \
|
|
11
|
+
build clean pre-commit ci e2e upgrade sync
|
|
12
12
|
|
|
13
13
|
## Show this help
|
|
14
14
|
help:
|
|
@@ -19,6 +19,10 @@ help:
|
|
|
19
19
|
install: ## install
|
|
20
20
|
$(PIP) install -e ".[dev]"
|
|
21
21
|
|
|
22
|
+
## Install full dev environment (package + build tooling, mirrors CI)
|
|
23
|
+
dev: ## dev
|
|
24
|
+
$(PIP) install -e ".[dev]" build twine
|
|
25
|
+
|
|
22
26
|
## Install pre-commit hooks
|
|
23
27
|
pre-commit: ## pre-commit
|
|
24
28
|
$(PIP) install pre-commit
|
|
@@ -34,18 +38,27 @@ cov: ## cov
|
|
|
34
38
|
|
|
35
39
|
## Run linter
|
|
36
40
|
lint: ## lint
|
|
37
|
-
$(RUFF) check src/
|
|
41
|
+
$(RUFF) check src/ tests/
|
|
38
42
|
|
|
39
43
|
## Fix lint issues automatically
|
|
40
44
|
lint-fix: ## lint-fix
|
|
41
|
-
$(RUFF) check src/ --fix
|
|
45
|
+
$(RUFF) check src/ tests/ --fix
|
|
46
|
+
|
|
47
|
+
## Type check
|
|
48
|
+
type-check: ## type-check
|
|
49
|
+
$(MYPY) src/
|
|
42
50
|
|
|
43
51
|
## Syntax check all modules
|
|
44
52
|
check-syntax: ## check-syntax
|
|
45
53
|
$(PYTHON) -m py_compile src/brandly_cli/*.py
|
|
46
54
|
|
|
47
|
-
##
|
|
48
|
-
|
|
55
|
+
## Build sdist + wheel (run 'make dev' first to install build/twine)
|
|
56
|
+
build: ## build
|
|
57
|
+
$(PYTHON) -m build
|
|
58
|
+
$(PYTHON) -m twine check dist/*
|
|
59
|
+
|
|
60
|
+
## Run all quality gates (lint + type-check + test + build)
|
|
61
|
+
ci: lint type-check test build ## ci
|
|
49
62
|
@echo "All quality gates passed."
|
|
50
63
|
|
|
51
64
|
## End-to-end CLI smoke test
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: brandly-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: AI product video orchestrator — add image, video, and sound generation to any AI tool
|
|
5
5
|
Project-URL: Homepage, https://github.com/Dream-Pixels-Forge/brandly-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/Dream-Pixels-Forge/brandly-cli
|
|
7
7
|
Project-URL: Issues, https://github.com/Dream-Pixels-Forge/brandly-cli/issues
|
|
8
8
|
Author-email: Dream Pixels Forge <contact@dreampixelsforge.com>
|
|
9
9
|
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
10
11
|
Keywords: agent,ai,cli,generation,image,orchestrator,video
|
|
11
12
|
Classifier: Development Status :: 4 - Beta
|
|
12
13
|
Classifier: Intended Audience :: Developers
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"""Built-in tool handlers for the Agnes AI agent loop.
|
|
2
|
+
|
|
3
|
+
Each tool is a Python callable that takes only JSON-serializable arguments
|
|
4
|
+
and returns a JSON-serializable result. The tool *descriptor* (name + JSON
|
|
5
|
+
schema) is exposed to the Agnes model via the OpenAI-compatible
|
|
6
|
+
``tools`` field, and this module provides the handler that actually
|
|
7
|
+
executes it on the client side.
|
|
8
|
+
|
|
9
|
+
Tools are intentionally small and read-only: they mirror the state the
|
|
10
|
+
brandly CLI already exposes (project data, Agnes jobs, model catalog)
|
|
11
|
+
so an agent can answer questions like "which projects have failed
|
|
12
|
+
videos?" without any new user input.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
from collections.abc import Awaitable, Callable
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
from rich.console import Console
|
|
23
|
+
|
|
24
|
+
console = Console()
|
|
25
|
+
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
# Tool handler type
|
|
28
|
+
#
|
|
29
|
+
# A handler is a function (sync or async) that takes keyword arguments and
|
|
30
|
+
# returns a JSON-serializable value. ``agent_tool_loop`` will await the
|
|
31
|
+
# result if it's a coroutine, and stringify non-strings with ``json.dumps``.
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
ToolHandler = Callable[..., Any] | Callable[..., Awaitable[Any]]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
# Tool: list_projects
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
def _list_projects(
|
|
42
|
+
status: str | None = None,
|
|
43
|
+
limit: int = 20,
|
|
44
|
+
root: str | None = None,
|
|
45
|
+
) -> dict[str, Any]:
|
|
46
|
+
"""List brandly projects on disk, optionally filtered by status."""
|
|
47
|
+
import asyncio
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
from brandly_cli.project_manager import ProjectManager
|
|
51
|
+
|
|
52
|
+
pm = ProjectManager(Path(root or "."))
|
|
53
|
+
projects = asyncio.run(pm.list_with_status())
|
|
54
|
+
if status:
|
|
55
|
+
projects = [p for p in projects if p.get("status") == status]
|
|
56
|
+
projects = projects[:limit]
|
|
57
|
+
return {
|
|
58
|
+
"count": len(projects),
|
|
59
|
+
"projects": [
|
|
60
|
+
{
|
|
61
|
+
"id": p.get("id", ""),
|
|
62
|
+
"name": p.get("name", ""),
|
|
63
|
+
"status": p.get("status", ""),
|
|
64
|
+
"updated_at": p.get("updated_at", ""),
|
|
65
|
+
}
|
|
66
|
+
for p in projects
|
|
67
|
+
],
|
|
68
|
+
}
|
|
69
|
+
except Exception as e: # pragma: no cover - safety net
|
|
70
|
+
return {"error": f"list_projects: {e}"}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# ---------------------------------------------------------------------------
|
|
74
|
+
# Tool: get_project
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
def _get_project(project_id: str, root: str | None = None) -> dict[str, Any]:
|
|
78
|
+
"""Fetch a single project by ID from the project manager."""
|
|
79
|
+
import asyncio
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
from brandly_cli.project_manager import ProjectManager
|
|
83
|
+
|
|
84
|
+
pm = ProjectManager(Path(root or "."))
|
|
85
|
+
data = asyncio.run(pm.read(project_id))
|
|
86
|
+
if not data:
|
|
87
|
+
return {"error": f"project {project_id} not found"}
|
|
88
|
+
model_dump = getattr(data, "model_dump", None)
|
|
89
|
+
return model_dump() if callable(model_dump) else {"id": project_id}
|
|
90
|
+
except Exception as e:
|
|
91
|
+
return {"error": f"get_project: {e}"}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# ---------------------------------------------------------------------------
|
|
95
|
+
# Tool: list_jobs (Agnes video jobs)
|
|
96
|
+
# ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
def _list_jobs(
|
|
99
|
+
status: str | None = None,
|
|
100
|
+
limit: int = 10,
|
|
101
|
+
) -> dict[str, Any]:
|
|
102
|
+
"""List recent Agnes video-generation jobs (read-only)."""
|
|
103
|
+
import asyncio
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
from brandly_cli.agnes_client import list_jobs
|
|
107
|
+
|
|
108
|
+
return {"jobs": asyncio.run(list_jobs(status=status, limit=limit))}
|
|
109
|
+
except Exception as e:
|
|
110
|
+
return {"error": f"list_jobs: {e}"}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# ---------------------------------------------------------------------------
|
|
114
|
+
# Tool: generate_image
|
|
115
|
+
# ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
def _generate_image(
|
|
118
|
+
prompt: str,
|
|
119
|
+
model: str = "agnes-image-2.5-flash",
|
|
120
|
+
size: str = "2K",
|
|
121
|
+
ratio: str = "16:9",
|
|
122
|
+
) -> dict[str, Any]:
|
|
123
|
+
"""Generate a single Agnes image. Synchronous wrapper over async."""
|
|
124
|
+
import asyncio
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
from brandly_cli.agnes_client import generate_image
|
|
128
|
+
|
|
129
|
+
return asyncio.run(
|
|
130
|
+
generate_image(prompt, model=model, size=size, ratio=ratio)
|
|
131
|
+
)
|
|
132
|
+
except Exception as e:
|
|
133
|
+
return {"error": f"generate_image: {e}"}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ---------------------------------------------------------------------------
|
|
137
|
+
# Tool: list_models
|
|
138
|
+
# ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
def _list_models(category: str | None = None) -> dict[str, Any]:
|
|
141
|
+
"""List available model IDs across image / video / audio / text."""
|
|
142
|
+
try:
|
|
143
|
+
from brandly_cli.constants import get_all_models
|
|
144
|
+
|
|
145
|
+
all_models = get_all_models()
|
|
146
|
+
if category and category in all_models:
|
|
147
|
+
return {"category": category, "models": all_models[category]}
|
|
148
|
+
return all_models
|
|
149
|
+
except Exception as e:
|
|
150
|
+
return {"error": f"list_models: {e}"}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# ---------------------------------------------------------------------------
|
|
154
|
+
# Tool registry
|
|
155
|
+
# ---------------------------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
BUILTIN_TOOLS: dict[str, dict[str, Any]] = {
|
|
158
|
+
"list_projects": {
|
|
159
|
+
"handler": _list_projects,
|
|
160
|
+
"schema": {
|
|
161
|
+
"type": "object",
|
|
162
|
+
"properties": {
|
|
163
|
+
"status": {
|
|
164
|
+
"type": "string",
|
|
165
|
+
"description": (
|
|
166
|
+
"Optional status filter (e.g. pending, running, failed)."
|
|
167
|
+
) ,
|
|
168
|
+
},
|
|
169
|
+
"limit": {
|
|
170
|
+
"type": "integer",
|
|
171
|
+
"default": 20,
|
|
172
|
+
"description": "Max projects to return.",
|
|
173
|
+
},
|
|
174
|
+
"root": {
|
|
175
|
+
"type": "string",
|
|
176
|
+
"description": "Project root directory (defaults to cwd).",
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
"description": "List brandly projects on disk, optionally filtered by status.",
|
|
181
|
+
},
|
|
182
|
+
"get_project": {
|
|
183
|
+
"handler": _get_project,
|
|
184
|
+
"schema": {
|
|
185
|
+
"type": "object",
|
|
186
|
+
"properties": {
|
|
187
|
+
"project_id": {
|
|
188
|
+
"type": "string",
|
|
189
|
+
"description": "UUID or slug of the project to fetch.",
|
|
190
|
+
},
|
|
191
|
+
"root": {
|
|
192
|
+
"type": "string",
|
|
193
|
+
"description": "Project root directory.",
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
"required": ["project_id"],
|
|
197
|
+
},
|
|
198
|
+
"description": "Fetch a single project by ID, including phases and cost tracking.",
|
|
199
|
+
},
|
|
200
|
+
"list_jobs": {
|
|
201
|
+
"handler": _list_jobs,
|
|
202
|
+
"schema": {
|
|
203
|
+
"type": "object",
|
|
204
|
+
"properties": {
|
|
205
|
+
"status": {
|
|
206
|
+
"type": "string",
|
|
207
|
+
"description": "Optional status filter.",
|
|
208
|
+
},
|
|
209
|
+
"limit": {
|
|
210
|
+
"type": "integer",
|
|
211
|
+
"default": 10,
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
"description": "List recent Agnes video-generation jobs (read-only).",
|
|
216
|
+
},
|
|
217
|
+
"generate_image": {
|
|
218
|
+
"handler": _generate_image,
|
|
219
|
+
"schema": {
|
|
220
|
+
"type": "object",
|
|
221
|
+
"properties": {
|
|
222
|
+
"prompt": {
|
|
223
|
+
"type": "string",
|
|
224
|
+
"description": "Text description of the image to generate.",
|
|
225
|
+
},
|
|
226
|
+
"model": {
|
|
227
|
+
"type": "string",
|
|
228
|
+
"default": "agnes-image-2.5-flash",
|
|
229
|
+
"description": "Agnes image model ID.",
|
|
230
|
+
},
|
|
231
|
+
"size": {
|
|
232
|
+
"type": "string",
|
|
233
|
+
"default": "2K",
|
|
234
|
+
"description": "Image size tier (1K/2K/3K/4K).",
|
|
235
|
+
},
|
|
236
|
+
"ratio": {
|
|
237
|
+
"type": "string",
|
|
238
|
+
"default": "16:9",
|
|
239
|
+
"description": "Aspect ratio (16:9, 9:16, 1:1, 4:3, 4:5).",
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
"required": ["prompt"],
|
|
243
|
+
},
|
|
244
|
+
"description": "Generate a single Agnes image from a text prompt.",
|
|
245
|
+
},
|
|
246
|
+
"list_models": {
|
|
247
|
+
"handler": _list_models,
|
|
248
|
+
"schema": {
|
|
249
|
+
"type": "object",
|
|
250
|
+
"properties": {
|
|
251
|
+
"category": {
|
|
252
|
+
"type": "string",
|
|
253
|
+
"enum": ["image", "video", "audio"],
|
|
254
|
+
"description": "Optional category filter.",
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
"description": "List available model IDs and metadata across image/video/audio.",
|
|
259
|
+
},
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def get_builtin_tools() -> list[tuple[str, ToolHandler, dict[str, Any], str]]:
|
|
264
|
+
"""Return the list of (name, handler, json_schema, description) tuples
|
|
265
|
+
expected by ``agent_tool_loop`` in ``agnes_client``.
|
|
266
|
+
|
|
267
|
+
The OpenAI-compatible ``tools`` payload requires:
|
|
268
|
+
{"type": "function", "function": {"name": ..., "description": ..., "parameters": {...}}}
|
|
269
|
+
so each entry is pre-shaped to drop in as-is.
|
|
270
|
+
"""
|
|
271
|
+
out: list[tuple[str, ToolHandler, dict[str, Any], str]] = []
|
|
272
|
+
for name, spec in BUILTIN_TOOLS.items():
|
|
273
|
+
out.append((name, spec["handler"], spec["schema"], spec["description"]))
|
|
274
|
+
return out
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def describe_tools() -> dict[str, Any]:
|
|
278
|
+
"""Human-readable summary of the built-in tool set, for CLI help."""
|
|
279
|
+
return {
|
|
280
|
+
name: {
|
|
281
|
+
"description": spec["description"],
|
|
282
|
+
"parameters": spec["schema"],
|
|
283
|
+
}
|
|
284
|
+
for name, spec in BUILTIN_TOOLS.items()
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def invoke_tool(name: str, **kwargs: Any) -> Any:
|
|
289
|
+
"""Invoke a named built-in tool with keyword args.
|
|
290
|
+
|
|
291
|
+
Returns the raw handler result (dict, list, str, …). Callers in
|
|
292
|
+
``agent_tool_loop`` will JSON-stringify non-string results.
|
|
293
|
+
"""
|
|
294
|
+
if name not in BUILTIN_TOOLS:
|
|
295
|
+
raise KeyError(f"Unknown tool: {name}. Available: {list(BUILTIN_TOOLS)}")
|
|
296
|
+
handler = BUILTIN_TOOLS[name]["handler"]
|
|
297
|
+
return handler(**kwargs)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
# ---------------------------------------------------------------------------
|
|
301
|
+
# JSON helpers
|
|
302
|
+
# ---------------------------------------------------------------------------
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def to_json(value: Any) -> str:
|
|
306
|
+
"""Stringify a tool result for feeding back to the LLM."""
|
|
307
|
+
if isinstance(value, str):
|
|
308
|
+
return value
|
|
309
|
+
try:
|
|
310
|
+
return json.dumps(value, default=str, ensure_ascii=False)
|
|
311
|
+
except (TypeError, ValueError):
|
|
312
|
+
return str(value)
|