diff --git a/.claude/settings.json b/.claude/settings.json index f4eb0331..644859f1 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -4,16 +4,16 @@ }, "enableAllProjectMcpServers": true, "enabledMcpjsonServers": [ - "tetra-code-intel" + "flash-code-intel" ], "permissions": { "allow": [ - "mcp__tetra-code-intel__find_by_decorator", - "mcp__tetra-code-intel__find_symbol", - "mcp__tetra-code-intel__get_class_interface", - "mcp__tetra-code-intel__list_classes", - "mcp__tetra-code-intel__list_file_symbols", - "mcp__tetra-code-intel__parse_test_output", + "mcp__flash-code-intel__find_by_decorator", + "mcp__flash-code-intel__find_symbol", + "mcp__flash-code-intel__get_class_interface", + "mcp__flash-code-intel__list_classes", + "mcp__flash-code-intel__list_file_symbols", + "mcp__flash-code-intel__parse_test_output", "Bash(./scripts/validate-wheel.sh:*)", "Bash(git diff:*)", "Bash(git fetch:*)", diff --git a/.claude/skills/tetra-explorer/SKILL.md b/.claude/skills/flash-explorer/SKILL.md similarity index 96% rename from .claude/skills/tetra-explorer/SKILL.md rename to .claude/skills/flash-explorer/SKILL.md index 77847a25..67e093b4 100644 --- a/.claude/skills/tetra-explorer/SKILL.md +++ b/.claude/skills/flash-explorer/SKILL.md @@ -1,9 +1,9 @@ -# Tetra-rp Framework Explorer Skill +# Flash Framework Explorer Skill ## When to Use Use this skill when: -- Exploring the tetra-rp framework codebase +- Exploring the runpod-flash framework codebase - Understanding class hierarchies and relationships - Finding where methods or classes are defined - Checking what decorators are used in the codebase @@ -74,7 +74,7 @@ After running tests (`make test-unit`, `make test`, `pytest`), **ALWAYS use `par 3. Only read the full file if you need implementation details **Bad - Reading files directly:** -1. Read entire `src/tetra_rp/core/resources/serverless.py` (500+ tokens) +1. Read entire `src/runpod_flash/core/resources/serverless.py` (500+ tokens) 2. Search manually for ServerlessEndpoint **Good - Parse test output:** diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index dee6f543..2b13b8f2 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -76,7 +76,7 @@ jobs: environment: name: pypi-production - url: https://pypi.org/project/tetra-rp/ + url: https://pypi.org/project/runpod-flash/ steps: - name: Checkout code diff --git a/.mcp.json b/.mcp.json index 333bdee8..6add59e9 100644 --- a/.mcp.json +++ b/.mcp.json @@ -1,6 +1,6 @@ { "mcpServers": { - "tetra-code-intel": { + "flash-code-intel": { "command": "uv", "args": [ "run", @@ -11,3 +11,4 @@ } } } + diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1cde6387..d5d3f370 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.25.2" + ".": "0.25.1" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 71a68496..a8ac8591 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,432 +1,417 @@ # Changelog -## [0.25.2](https://github.com/runpod/tetra-rp/compare/v0.25.1...v0.25.2) (2026-02-04) +## [0.25.1](https://github.com/runpod/runpod-flash/compare/v0.25.0...v0.25.1) (2026-02-03) ### Bug Fixes -* 0.25.3 ([#173](https://github.com/runpod/tetra-rp/issues/173)) ([98b1ed7](https://github.com/runpod/tetra-rp/commit/98b1ed7c98ee8bbab43b4c880210579c1cab6a37)) +* set build id for env before deploying resources ([#169](https://github.com/runpod/runpod-flash/issues/169)) ([e5d38d6](https://github.com/runpod/runpod-flash/commit/e5d38d61c6daf12a482341c5675b9125431f6d05)) -## [0.25.2](https://github.com/runpod/tetra-rp/compare/v0.25.1...v0.25.2) (2026-02-03) - -### ⚠️ DEPRECATED - -This is the final release of `tetra-rp`. All future development and releases will be under the new package name `runpod-flash`. See [runpod/flash](https://github.com/runpod/flash) for the new repository. - -**Migration guide:** Uninstall `tetra-rp`, install `runpod-flash`, and update imports from `from tetra_rp import ...` to `from runpod_flash import ...` - -## [0.25.1](https://github.com/runpod/tetra-rp/compare/v0.25.0...v0.25.1) (2026-02-03) - - -### Bug Fixes - -* set build id for env before deploying resources ([#169](https://github.com/runpod/tetra-rp/issues/169)) ([e5d38d6](https://github.com/runpod/tetra-rp/commit/e5d38d61c6daf12a482341c5675b9125431f6d05)) - -## [0.25.0](https://github.com/runpod/tetra-rp/compare/v0.24.0...v0.25.0) (2026-02-02) +## [0.25.0](https://github.com/runpod/runpod-flash/compare/v0.24.0...v0.25.0) (2026-02-02) ### Features -* `flash build --preview` in local docker for smoke-testing deployments ([#167](https://github.com/runpod/tetra-rp/issues/167)) ([a2cc56c](https://github.com/runpod/tetra-rp/commit/a2cc56c755ae6badb4f00ad8fe62c2e3d89af9bd)) -* code intelligence indexer service for Claude Code ([#158](https://github.com/runpod/tetra-rp/issues/158)) ([69b3887](https://github.com/runpod/tetra-rp/commit/69b38875b9991d8e8095b39d98382f99487adfad)) +* `flash build --preview` in local docker for smoke-testing deployments ([#167](https://github.com/runpod/runpod-flash/issues/167)) ([a2cc56c](https://github.com/runpod/runpod-flash/commit/a2cc56c755ae6badb4f00ad8fe62c2e3d89af9bd)) +* code intelligence indexer service for Claude Code ([#158](https://github.com/runpod/runpod-flash/issues/158)) ([69b3887](https://github.com/runpod/runpod-flash/commit/69b38875b9991d8e8095b39d98382f99487adfad)) ### Bug Fixes -* exclude NetworkVolume from undeploy list command ([#164](https://github.com/runpod/tetra-rp/issues/164)) ([b0960d9](https://github.com/runpod/tetra-rp/commit/b0960d967b588b43c0970a42e8fa8c69c7e83077)) +* exclude NetworkVolume from undeploy list command ([#164](https://github.com/runpod/runpod-flash/issues/164)) ([b0960d9](https://github.com/runpod/runpod-flash/commit/b0960d967b588b43c0970a42e8fa8c69c7e83077)) ### Code Refactoring -* **build:** remove deprecated handler generator and add verification scripts ([#166](https://github.com/runpod/tetra-rp/issues/166)) ([5a253af](https://github.com/runpod/tetra-rp/commit/5a253af047052acdf1874072ddb8864bbdfa1679)) -* rename archive.tar.gz to artifact.tar.gz ([#170](https://github.com/runpod/tetra-rp/issues/170)) ([1f033c3](https://github.com/runpod/tetra-rp/commit/1f033c39ec71f1291d5c261aa5d88bb0182c2270)) +* **build:** remove deprecated handler generator and add verification scripts ([#166](https://github.com/runpod/runpod-flash/issues/166)) ([5a253af](https://github.com/runpod/runpod-flash/commit/5a253af047052acdf1874072ddb8864bbdfa1679)) +* rename archive.tar.gz to artifact.tar.gz ([#170](https://github.com/runpod/runpod-flash/issues/170)) ([1f033c3](https://github.com/runpod/runpod-flash/commit/1f033c39ec71f1291d5c261aa5d88bb0182c2270)) -## [0.24.0](https://github.com/runpod/tetra-rp/compare/v0.23.1...v0.24.0) (2026-01-30) +## [0.24.0](https://github.com/runpod/runpod-flash/compare/v0.23.1...v0.24.0) (2026-01-30) ### Features -* undeploy resources during flash deploy delete ([#155](https://github.com/runpod/tetra-rp/issues/155)) ([5256821](https://github.com/runpod/tetra-rp/commit/5256821c8698116b6b05c8f7b836cecfccf0d22f)) +* undeploy resources during flash deploy delete ([#155](https://github.com/runpod/runpod-flash/issues/155)) ([5256821](https://github.com/runpod/runpod-flash/commit/5256821c8698116b6b05c8f7b836cecfccf0d22f)) -## [0.23.1](https://github.com/runpod/tetra-rp/compare/v0.23.0...v0.23.1) (2026-01-29) +## [0.23.1](https://github.com/runpod/runpod-flash/compare/v0.23.0...v0.23.1) (2026-01-29) ### Bug Fixes -* add flash env id to saveendpoint during flash deploy ([#161](https://github.com/runpod/tetra-rp/issues/161)) ([d5c000e](https://github.com/runpod/tetra-rp/commit/d5c000e861d81a68cdb5f2426a989ebc3ddfc174)) +* add flash env id to saveendpoint during flash deploy ([#161](https://github.com/runpod/runpod-flash/issues/161)) ([d5c000e](https://github.com/runpod/runpod-flash/commit/d5c000e861d81a68cdb5f2426a989ebc3ddfc174)) -## [0.23.0](https://github.com/runpod/tetra-rp/compare/v0.22.0...v0.23.0) (2026-01-29) +## [0.23.0](https://github.com/runpod/runpod-flash/compare/v0.22.0...v0.23.0) (2026-01-29) ### Features -* disable EU-RO-1 lock in non production environments ([5f4772d](https://github.com/runpod/tetra-rp/commit/5f4772df988da5db54476cc9f14f71cb6727f516)) -* pull in endpoint base from runpod-python ([a970406](https://github.com/runpod/tetra-rp/commit/a970406444af3db4a8232a4903d87332a2fd12ab)) -* support specific GPU ids in serverless resource config ([#143](https://github.com/runpod/tetra-rp/issues/143)) ([86fde45](https://github.com/runpod/tetra-rp/commit/86fde45d64e3936a79d41be19321088f0e4f4c96)) +* disable EU-RO-1 lock in non production environments ([5f4772d](https://github.com/runpod/runpod-flash/commit/5f4772df988da5db54476cc9f14f71cb6727f516)) +* pull in endpoint base from runpod-python ([a970406](https://github.com/runpod/runpod-flash/commit/a970406444af3db4a8232a4903d87332a2fd12ab)) +* support specific GPU ids in serverless resource config ([#143](https://github.com/runpod/runpod-flash/issues/143)) ([86fde45](https://github.com/runpod/runpod-flash/commit/86fde45d64e3936a79d41be19321088f0e4f4c96)) ### Bug Fixes -* **deploy:** apply CPU disk auto-sizing to load balancers ([#156](https://github.com/runpod/tetra-rp/issues/156)) ([334d582](https://github.com/runpod/tetra-rp/commit/334d5825592f514482c95c4b6f15a4f25ce03ba5)) -* fix usage on VPNs because aiodns is flaky ([c55c97b](https://github.com/runpod/tetra-rp/commit/c55c97b0696aeb3632ce8623d1a1b8c9058831cf)) +* **deploy:** apply CPU disk auto-sizing to load balancers ([#156](https://github.com/runpod/runpod-flash/issues/156)) ([334d582](https://github.com/runpod/runpod-flash/commit/334d5825592f514482c95c4b6f15a4f25ce03ba5)) +* fix usage on VPNs because aiodns is flaky ([c55c97b](https://github.com/runpod/runpod-flash/commit/c55c97b0696aeb3632ce8623d1a1b8c9058831cf)) -## [0.22.0](https://github.com/runpod/tetra-rp/compare/v0.21.0...v0.22.0) (2026-01-27) +## [0.22.0](https://github.com/runpod/runpod-flash/compare/v0.21.0...v0.22.0) (2026-01-27) ### Features -* implement cross-endpoint routing reliability infrastructure ([#149](https://github.com/runpod/tetra-rp/issues/149)) ([cb6a226](https://github.com/runpod/tetra-rp/commit/cb6a2268836d449e4d3445ef787c5b7c184bbea6)) -* implement upfront resource provisioning in deployment flow ([#152](https://github.com/runpod/tetra-rp/issues/152)) ([b9c5571](https://github.com/runpod/tetra-rp/commit/b9c5571db3e260b0456e62dd77329db6e754822b)) +* implement cross-endpoint routing reliability infrastructure ([#149](https://github.com/runpod/runpod-flash/issues/149)) ([cb6a226](https://github.com/runpod/runpod-flash/commit/cb6a2268836d449e4d3445ef787c5b7c184bbea6)) +* implement upfront resource provisioning in deployment flow ([#152](https://github.com/runpod/runpod-flash/issues/152)) ([b9c5571](https://github.com/runpod/runpod-flash/commit/b9c5571db3e260b0456e62dd77329db6e754822b)) -## [0.21.0](https://github.com/runpod/tetra-rp/compare/v0.20.1...v0.21.0) (2026-01-26) +## [0.21.0](https://github.com/runpod/runpod-flash/compare/v0.20.1...v0.21.0) (2026-01-26) ### Features -* add "live-" prefix to live serverless endpoints ([#150](https://github.com/runpod/tetra-rp/issues/150)) ([5c8fa33](https://github.com/runpod/tetra-rp/commit/5c8fa33326eb6f4d1bff4cc8d8e09d7eb97821dc)) +* add "live-" prefix to live serverless endpoints ([#150](https://github.com/runpod/runpod-flash/issues/150)) ([5c8fa33](https://github.com/runpod/runpod-flash/commit/5c8fa33326eb6f4d1bff4cc8d8e09d7eb97821dc)) -## [0.20.1](https://github.com/runpod/tetra-rp/compare/v0.20.0...v0.20.1) (2026-01-22) +## [0.20.1](https://github.com/runpod/runpod-flash/compare/v0.20.0...v0.20.1) (2026-01-22) ### Performance Improvements -* **tests:** make parallel test execution the default ([#145](https://github.com/runpod/tetra-rp/issues/145)) ([3faf170](https://github.com/runpod/tetra-rp/commit/3faf1706fa7252c56c8fab68e7dc95b570845245)) +* **tests:** make parallel test execution the default ([#145](https://github.com/runpod/runpod-flash/issues/145)) ([3faf170](https://github.com/runpod/runpod-flash/commit/3faf1706fa7252c56c8fab68e7dc95b570845245)) -## [0.20.0](https://github.com/runpod/tetra-rp/compare/v0.19.0...v0.20.0) (2026-01-15) +## [0.20.0](https://github.com/runpod/runpod-flash/compare/v0.19.0...v0.20.0) (2026-01-15) ### Features -* add generic handler factory and build integration for Flash ([#130](https://github.com/runpod/tetra-rp/issues/130)) ([8c1e6b8](https://github.com/runpod/tetra-rp/commit/8c1e6b86022a0c5f91dcf1832adf467607004f01)) -* add GET /manifest endpoint for mothership service discovery ([#139](https://github.com/runpod/tetra-rp/issues/139)) ([2956f09](https://github.com/runpod/tetra-rp/commit/2956f09318b459183b21a387c10d49bad03f19ee)) -* AE-1741 manifest management via gql for flash client ([#144](https://github.com/runpod/tetra-rp/issues/144)) ([83979e7](https://github.com/runpod/tetra-rp/commit/83979e7a80e6789931f555cd882ca77398a43615)) -* **build:** Add cross-platform build support and package exclusion ([#135](https://github.com/runpod/tetra-rp/issues/135)) ([68e0539](https://github.com/runpod/tetra-rp/commit/68e05391250a1232d4382baefedea81e45ca2f66)) -* complete `[@remote](https://github.com/remote)` support for LoadBalancer endpoints ([#131](https://github.com/runpod/tetra-rp/issues/131)) ([f2f34c0](https://github.com/runpod/tetra-rp/commit/f2f34c07b0d02a7c42c51789cb25c2f5eaaacc41)) -* cross-endpoint routing for serverless functions ([#129](https://github.com/runpod/tetra-rp/issues/129)) ([57ff437](https://github.com/runpod/tetra-rp/commit/57ff437f916ffbd0b29ec989a0361d6af674ca58)) -* mothership manifest sync and caching ([#140](https://github.com/runpod/tetra-rp/issues/140)) ([20490ea](https://github.com/runpod/tetra-rp/commit/20490ea3a749e93a33daf41cc98cebbc30669b5b)) -* **mothership:** implement auto-provisioning with manifest ([#136](https://github.com/runpod/tetra-rp/issues/136)) ([14effd4](https://github.com/runpod/tetra-rp/commit/14effd4ab5ed506206c36153dd9c72485deafa37)) +* add generic handler factory and build integration for Flash ([#130](https://github.com/runpod/runpod-flash/issues/130)) ([8c1e6b8](https://github.com/runpod/runpod-flash/commit/8c1e6b86022a0c5f91dcf1832adf467607004f01)) +* add GET /manifest endpoint for mothership service discovery ([#139](https://github.com/runpod/runpod-flash/issues/139)) ([2956f09](https://github.com/runpod/runpod-flash/commit/2956f09318b459183b21a387c10d49bad03f19ee)) +* AE-1741 manifest management via gql for flash client ([#144](https://github.com/runpod/runpod-flash/issues/144)) ([83979e7](https://github.com/runpod/runpod-flash/commit/83979e7a80e6789931f555cd882ca77398a43615)) +* **build:** Add cross-platform build support and package exclusion ([#135](https://github.com/runpod/runpod-flash/issues/135)) ([68e0539](https://github.com/runpod/runpod-flash/commit/68e05391250a1232d4382baefedea81e45ca2f66)) +* complete `[@remote](https://github.com/remote)` support for LoadBalancer endpoints ([#131](https://github.com/runpod/runpod-flash/issues/131)) ([f2f34c0](https://github.com/runpod/runpod-flash/commit/f2f34c07b0d02a7c42c51789cb25c2f5eaaacc41)) +* cross-endpoint routing for serverless functions ([#129](https://github.com/runpod/runpod-flash/issues/129)) ([57ff437](https://github.com/runpod/runpod-flash/commit/57ff437f916ffbd0b29ec989a0361d6af674ca58)) +* mothership manifest sync and caching ([#140](https://github.com/runpod/runpod-flash/issues/140)) ([20490ea](https://github.com/runpod/runpod-flash/commit/20490ea3a749e93a33daf41cc98cebbc30669b5b)) +* **mothership:** implement auto-provisioning with manifest ([#136](https://github.com/runpod/runpod-flash/issues/136)) ([14effd4](https://github.com/runpod/runpod-flash/commit/14effd4ab5ed506206c36153dd9c72485deafa37)) ### Bug Fixes -* **drift:** Exclude runtime fields from config hash to prevent false positives ([#132](https://github.com/runpod/tetra-rp/issues/132)) ([13ced50](https://github.com/runpod/tetra-rp/commit/13ced50558be287c235b272f3419babf168b6af1)) +* **drift:** Exclude runtime fields from config hash to prevent false positives ([#132](https://github.com/runpod/runpod-flash/issues/132)) ([13ced50](https://github.com/runpod/runpod-flash/commit/13ced50558be287c235b272f3419babf168b6af1)) ### Documentation -* **architecture:** Add deployment architecture specification ([#141](https://github.com/runpod/tetra-rp/issues/141)) ([b1de925](https://github.com/runpod/tetra-rp/commit/b1de9251a5c90dbfdfe7288a161f7bef51b4fa7f)) +* **architecture:** Add deployment architecture specification ([#141](https://github.com/runpod/runpod-flash/issues/141)) ([b1de925](https://github.com/runpod/runpod-flash/commit/b1de9251a5c90dbfdfe7288a161f7bef51b4fa7f)) -## [0.19.0](https://github.com/runpod/tetra-rp/compare/v0.18.0...v0.19.0) (2025-12-12) +## [0.19.0](https://github.com/runpod/runpod-flash/compare/v0.18.0...v0.19.0) (2025-12-12) ### Features -* AE-1512: deploy() and undeploy() deployable resources directly ([#126](https://github.com/runpod/tetra-rp/issues/126)) ([3deac3a](https://github.com/runpod/tetra-rp/commit/3deac3a91b84fa4cf07cf553c46431907290a61c)) -* **cli:** Add --auto-provision flag to flash run command ([#125](https://github.com/runpod/tetra-rp/issues/125)) ([ee5793c](https://github.com/runpod/tetra-rp/commit/ee5793c33537acc15e26b680e3bac5aedb3c0735)) +* AE-1512: deploy() and undeploy() deployable resources directly ([#126](https://github.com/runpod/runpod-flash/issues/126)) ([3deac3a](https://github.com/runpod/runpod-flash/commit/3deac3a91b84fa4cf07cf553c46431907290a61c)) +* **cli:** Add --auto-provision flag to flash run command ([#125](https://github.com/runpod/runpod-flash/issues/125)) ([ee5793c](https://github.com/runpod/runpod-flash/commit/ee5793c33537acc15e26b680e3bac5aedb3c0735)) ### Code Refactoring -* use env vars FLASH_HOST and FLASH_PORT ([#128](https://github.com/runpod/tetra-rp/issues/128)) ([117a6ae](https://github.com/runpod/tetra-rp/commit/117a6aea91b9ca53fc3671150f746766307dbab4)) +* use env vars FLASH_HOST and FLASH_PORT ([#128](https://github.com/runpod/runpod-flash/issues/128)) ([117a6ae](https://github.com/runpod/runpod-flash/commit/117a6aea91b9ca53fc3671150f746766307dbab4)) -## [0.18.0](https://github.com/runpod/tetra-rp/compare/v0.17.1...v0.18.0) (2025-11-28) +## [0.18.0](https://github.com/runpod/runpod-flash/compare/v0.17.1...v0.18.0) (2025-11-28) ### Features -* **cli:** Add `flash undeploy` command for endpoint management ([#121](https://github.com/runpod/tetra-rp/issues/121)) ([cd32ffc](https://github.com/runpod/tetra-rp/commit/cd32ffc40ac26c2f1aaa1235b044798ec7b9f605)) +* **cli:** Add `flash undeploy` command for endpoint management ([#121](https://github.com/runpod/runpod-flash/issues/121)) ([cd32ffc](https://github.com/runpod/runpod-flash/commit/cd32ffc40ac26c2f1aaa1235b044798ec7b9f605)) ### Code Refactoring -* move endpoint deletion logic to proper abstraction layers ([#124](https://github.com/runpod/tetra-rp/issues/124)) ([c253d3b](https://github.com/runpod/tetra-rp/commit/c253d3b6f959dc8514b91b5e79f6f73fe9593b89)) +* move endpoint deletion logic to proper abstraction layers ([#124](https://github.com/runpod/runpod-flash/issues/124)) ([c253d3b](https://github.com/runpod/runpod-flash/commit/c253d3b6f959dc8514b91b5e79f6f73fe9593b89)) -## [0.17.1](https://github.com/runpod/tetra-rp/compare/v0.17.0...v0.17.1) (2025-11-19) +## [0.17.1](https://github.com/runpod/runpod-flash/compare/v0.17.0...v0.17.1) (2025-11-19) ### Documentation -* update readme for alpha release ([#120](https://github.com/runpod/tetra-rp/issues/120)) ([e8093d8](https://github.com/runpod/tetra-rp/commit/e8093d8ac878c13e578dc0ded823dfe8c73120f3)) +* update readme for alpha release ([#120](https://github.com/runpod/runpod-flash/issues/120)) ([e8093d8](https://github.com/runpod/runpod-flash/commit/e8093d8ac878c13e578dc0ded823dfe8c73120f3)) -## [0.17.0](https://github.com/runpod/tetra-rp/compare/v0.16.1...v0.17.0) (2025-11-19) +## [0.17.0](https://github.com/runpod/runpod-flash/compare/v0.16.1...v0.17.0) (2025-11-19) ### Features -* add user-friendly error messages for missing RUNPOD_API_KEY ([#117](https://github.com/runpod/tetra-rp/issues/117)) ([32dc093](https://github.com/runpod/tetra-rp/commit/32dc0937dda02a8b6178f3f5b2219f18de1f933e)) +* add user-friendly error messages for missing RUNPOD_API_KEY ([#117](https://github.com/runpod/runpod-flash/issues/117)) ([32dc093](https://github.com/runpod/runpod-flash/commit/32dc0937dda02a8b6178f3f5b2219f18de1f933e)) ### Documentation -* improved flash init skeleton, contributing, and misc cleanup ([#118](https://github.com/runpod/tetra-rp/issues/118)) ([91acf1a](https://github.com/runpod/tetra-rp/commit/91acf1a87b5cdcf7f66c2bfa976f6132a8ca3cea)) +* improved flash init skeleton, contributing, and misc cleanup ([#118](https://github.com/runpod/runpod-flash/issues/118)) ([91acf1a](https://github.com/runpod/runpod-flash/commit/91acf1a87b5cdcf7f66c2bfa976f6132a8ca3cea)) -## [0.16.1](https://github.com/runpod/tetra-rp/compare/v0.16.0...v0.16.1) (2025-11-14) +## [0.16.1](https://github.com/runpod/runpod-flash/compare/v0.16.0...v0.16.1) (2025-11-14) ### Bug Fixes -* **skeleton:** Fix flash init missing hidden files in wheel distributions ([#115](https://github.com/runpod/tetra-rp/issues/115)) ([c3bf137](https://github.com/runpod/tetra-rp/commit/c3bf1376382cad8dcfb5c33d33e8876b97585384)) +* **skeleton:** Fix flash init missing hidden files in wheel distributions ([#115](https://github.com/runpod/runpod-flash/issues/115)) ([c3bf137](https://github.com/runpod/runpod-flash/commit/c3bf1376382cad8dcfb5c33d33e8876b97585384)) -## [0.16.0](https://github.com/runpod/tetra-rp/compare/v0.15.0...v0.16.0) (2025-11-14) +## [0.16.0](https://github.com/runpod/runpod-flash/compare/v0.15.0...v0.16.0) (2025-11-14) ### Features -* **cli:** Add flash init with project skeleton template and in-place initialization ([#110](https://github.com/runpod/tetra-rp/issues/110)) ([155d6ee](https://github.com/runpod/tetra-rp/commit/155d6ee64014936c082173751d0978c7cba39092)) +* **cli:** Add flash init with project skeleton template and in-place initialization ([#110](https://github.com/runpod/runpod-flash/issues/110)) ([155d6ee](https://github.com/runpod/runpod-flash/commit/155d6ee64014936c082173751d0978c7cba39092)) -## [0.15.0](https://github.com/runpod/tetra-rp/compare/v0.14.0...v0.15.0) (2025-11-14) +## [0.15.0](https://github.com/runpod/runpod-flash/compare/v0.14.0...v0.15.0) (2025-11-14) ### Features -* **client:** add async function support to remote decorator ([#112](https://github.com/runpod/tetra-rp/issues/112)) ([b0222e0](https://github.com/runpod/tetra-rp/commit/b0222e006ca9b3e7cd8ea0b55804dcabf6d8fce8)) -* **resources:** Support for Serverless.type QB|LB ([#109](https://github.com/runpod/tetra-rp/issues/109)) ([6e63459](https://github.com/runpod/tetra-rp/commit/6e63459d1a174836912c7b72590341ea6b3cf2b6)) +* **client:** add async function support to remote decorator ([#112](https://github.com/runpod/runpod-flash/issues/112)) ([b0222e0](https://github.com/runpod/runpod-flash/commit/b0222e006ca9b3e7cd8ea0b55804dcabf6d8fce8)) +* **resources:** Support for Serverless.type QB|LB ([#109](https://github.com/runpod/runpod-flash/issues/109)) ([6e63459](https://github.com/runpod/runpod-flash/commit/6e63459d1a174836912c7b72590341ea6b3cf2b6)) -## [0.14.0](https://github.com/runpod/tetra-rp/compare/v0.13.0...v0.14.0) (2025-10-31) +## [0.14.0](https://github.com/runpod/runpod-flash/compare/v0.13.0...v0.14.0) (2025-10-31) ### Features -* AE-1202 add flash cli cmd init and run ([#96](https://github.com/runpod/tetra-rp/issues/96)) ([75b2baf](https://github.com/runpod/tetra-rp/commit/75b2baf4a35c9e6f3fc973ed287bd8da9285c607)) +* AE-1202 add flash cli cmd init and run ([#96](https://github.com/runpod/runpod-flash/issues/96)) ([75b2baf](https://github.com/runpod/runpod-flash/commit/75b2baf4a35c9e6f3fc973ed287bd8da9285c607)) -## [0.13.0](https://github.com/runpod/tetra-rp/compare/v0.12.0...v0.13.0) (2025-10-09) +## [0.13.0](https://github.com/runpod/runpod-flash/compare/v0.12.0...v0.13.0) (2025-10-09) ### Features -* Command Line Interface ([#50](https://github.com/runpod/tetra-rp/issues/50)) ([5f4cde6](https://github.com/runpod/tetra-rp/commit/5f4cde6b0a8a7e082a0a1e0ce184d677309f0cfd)) +* Command Line Interface ([#50](https://github.com/runpod/runpod-flash/issues/50)) ([5f4cde6](https://github.com/runpod/runpod-flash/commit/5f4cde6b0a8a7e082a0a1e0ce184d677309f0cfd)) ### Bug Fixes -* repair build and release actions ([#97](https://github.com/runpod/tetra-rp/issues/97)) ([4a73fa8](https://github.com/runpod/tetra-rp/commit/4a73fa811729b612f9367e4b2c16831b553e00eb)) +* repair build and release actions ([#97](https://github.com/runpod/runpod-flash/issues/97)) ([4a73fa8](https://github.com/runpod/runpod-flash/commit/4a73fa811729b612f9367e4b2c16831b553e00eb)) ### Code Refactoring -* deprecate hf_models_to_cache ([#95](https://github.com/runpod/tetra-rp/issues/95)) ([963bfd7](https://github.com/runpod/tetra-rp/commit/963bfd7ee25f204b85888f09e444b1ecf0b75ffb)) +* deprecate hf_models_to_cache ([#95](https://github.com/runpod/runpod-flash/issues/95)) ([963bfd7](https://github.com/runpod/runpod-flash/commit/963bfd7ee25f204b85888f09e444b1ecf0b75ffb)) -## [0.12.0](https://github.com/runpod/tetra-rp/compare/v0.11.0...v0.12.0) (2025-09-15) +## [0.12.0](https://github.com/runpod/runpod-flash/compare/v0.11.0...v0.12.0) (2025-09-15) ### Features -* better clarity on provisioning CPU Serverless Endpoints ([#88](https://github.com/runpod/tetra-rp/issues/88)) ([efec224](https://github.com/runpod/tetra-rp/commit/efec224029e8c03133f8f4840b38308f141743ce)) -* thread-safe resource provisions and remote executions ([#91](https://github.com/runpod/tetra-rp/issues/91)) ([440b36f](https://github.com/runpod/tetra-rp/commit/440b36f6e15bffc68f1f77589d7b8fa4d6fc2025)) +* better clarity on provisioning CPU Serverless Endpoints ([#88](https://github.com/runpod/runpod-flash/issues/88)) ([efec224](https://github.com/runpod/runpod-flash/commit/efec224029e8c03133f8f4840b38308f141743ce)) +* thread-safe resource provisions and remote executions ([#91](https://github.com/runpod/runpod-flash/issues/91)) ([440b36f](https://github.com/runpod/runpod-flash/commit/440b36f6e15bffc68f1f77589d7b8fa4d6fc2025)) ### Bug Fixes -* download accelerator changes broken regular endpoint invocations ([#86](https://github.com/runpod/tetra-rp/issues/86)) ([759f996](https://github.com/runpod/tetra-rp/commit/759f996208ebb5f052cda5e8b52b8c3b7a542b26)) +* download accelerator changes broken regular endpoint invocations ([#86](https://github.com/runpod/runpod-flash/issues/86)) ([759f996](https://github.com/runpod/runpod-flash/commit/759f996208ebb5f052cda5e8b52b8c3b7a542b26)) -## [0.11.0](https://github.com/runpod/tetra-rp/compare/v0.10.0...v0.11.0) (2025-08-19) +## [0.11.0](https://github.com/runpod/runpod-flash/compare/v0.10.0...v0.11.0) (2025-08-19) ### Features -* Add download acceleration for dependencies and HuggingFace models ([#83](https://github.com/runpod/tetra-rp/issues/83)) ([e47c9e3](https://github.com/runpod/tetra-rp/commit/e47c9e37030ead1831893dd70a1322421befbaad)) +* Add download acceleration for dependencies and HuggingFace models ([#83](https://github.com/runpod/runpod-flash/issues/83)) ([e47c9e3](https://github.com/runpod/runpod-flash/commit/e47c9e37030ead1831893dd70a1322421befbaad)) -## [0.10.0](https://github.com/runpod/tetra-rp/compare/v0.9.0...v0.10.0) (2025-08-07) +## [0.10.0](https://github.com/runpod/runpod-flash/compare/v0.9.0...v0.10.0) (2025-08-07) ### Features -* Add idempotent network volume deployment ([#79](https://github.com/runpod/tetra-rp/issues/79)) ([289d333](https://github.com/runpod/tetra-rp/commit/289d333aaaf48e00bfdad2a5f6356bdfc6bcf286)) +* Add idempotent network volume deployment ([#79](https://github.com/runpod/runpod-flash/issues/79)) ([289d333](https://github.com/runpod/runpod-flash/commit/289d333aaaf48e00bfdad2a5f6356bdfc6bcf286)) -## [0.9.0](https://github.com/runpod/tetra-rp/compare/v0.8.0...v0.9.0) (2025-08-04) +## [0.9.0](https://github.com/runpod/runpod-flash/compare/v0.8.0...v0.9.0) (2025-08-04) ### Features -* AE-961 Add class serialization caching for remote execution ([#76](https://github.com/runpod/tetra-rp/issues/76)) ([95f9eed](https://github.com/runpod/tetra-rp/commit/95f9eed1810e6a623091348c326e2ea571c6dddf)) +* AE-961 Add class serialization caching for remote execution ([#76](https://github.com/runpod/runpod-flash/issues/76)) ([95f9eed](https://github.com/runpod/runpod-flash/commit/95f9eed1810e6a623091348c326e2ea571c6dddf)) -## [0.8.0](https://github.com/runpod/tetra-rp/compare/v0.7.0...v0.8.0) (2025-07-22) +## [0.8.0](https://github.com/runpod/runpod-flash/compare/v0.7.0...v0.8.0) (2025-07-22) ### Features -* AE-815 Add class based execution ([#72](https://github.com/runpod/tetra-rp/issues/72)) ([d2a70ad](https://github.com/runpod/tetra-rp/commit/d2a70ad702bfd7e8f9c137eb2ce8263d1dfbd667)) +* AE-815 Add class based execution ([#72](https://github.com/runpod/runpod-flash/issues/72)) ([d2a70ad](https://github.com/runpod/runpod-flash/commit/d2a70ad702bfd7e8f9c137eb2ce8263d1dfbd667)) -## [0.7.0](https://github.com/runpod/tetra-rp/compare/0.6.0...v0.7.0) (2025-07-21) +## [0.7.0](https://github.com/runpod/runpod-flash/compare/0.6.0...v0.7.0) (2025-07-21) ### Features -* AE-892 - Lock the network volume region ([#67](https://github.com/runpod/tetra-rp/issues/67)) ([2b7a3ea](https://github.com/runpod/tetra-rp/commit/2b7a3eae2b1f4b343b20a1548e63507e88b1adbc)) +* AE-892 - Lock the network volume region ([#67](https://github.com/runpod/runpod-flash/issues/67)) ([2b7a3ea](https://github.com/runpod/runpod-flash/commit/2b7a3eae2b1f4b343b20a1548e63507e88b1adbc)) ### Bug Fixes -* downgrade release-please to v3 for simplicity ([#70](https://github.com/runpod/tetra-rp/issues/70)) ([06f05d1](https://github.com/runpod/tetra-rp/commit/06f05d1bb0e7fca9208a86248432e9d72a20036a)) -* use default release-please PR title pattern ([#68](https://github.com/runpod/tetra-rp/issues/68)) ([786df98](https://github.com/runpod/tetra-rp/commit/786df986d2f36d324bd470937aa9dae7c0cc38be)) +* downgrade release-please to v3 for simplicity ([#70](https://github.com/runpod/runpod-flash/issues/70)) ([06f05d1](https://github.com/runpod/runpod-flash/commit/06f05d1bb0e7fca9208a86248432e9d72a20036a)) +* use default release-please PR title pattern ([#68](https://github.com/runpod/runpod-flash/issues/68)) ([786df98](https://github.com/runpod/runpod-flash/commit/786df986d2f36d324bd470937aa9dae7c0cc38be)) -## [0.6.0](https://github.com/runpod/tetra-rp/compare/0.5.5...0.6.0) (2025-07-10) +## [0.6.0](https://github.com/runpod/runpod-flash/compare/0.5.5...0.6.0) (2025-07-10) ### Features -* AE-811 Add a class to create network volume using REST ([abb82b7](https://github.com/runpod/tetra-rp/commit/abb82b74bf232833d96baee56d2b352202223b6e)) +* AE-811 Add a class to create network volume using REST ([abb82b7](https://github.com/runpod/runpod-flash/commit/abb82b74bf232833d96baee56d2b352202223b6e)) -## [0.5.5](https://github.com/runpod/tetra-rp/compare/0.5.4...0.5.5) (2025-07-10) +## [0.5.5](https://github.com/runpod/runpod-flash/compare/0.5.4...0.5.5) (2025-07-10) ### Bug Fixes -* remove sigstore signing to simplify PyPI publishing ([65037c5](https://github.com/runpod/tetra-rp/commit/65037c5f4c11bb8a08292b98f17d1f005784e5ed)) +* remove sigstore signing to simplify PyPI publishing ([65037c5](https://github.com/runpod/runpod-flash/commit/65037c5f4c11bb8a08292b98f17d1f005784e5ed)) -## [0.5.4](https://github.com/runpod/tetra-rp/compare/0.5.3...0.5.4) (2025-07-10) +## [0.5.4](https://github.com/runpod/runpod-flash/compare/0.5.3...0.5.4) (2025-07-10) ### Bug Fixes -* revert to simple working CI/release workflows, remove broken shared workflow ([3fe4cf0](https://github.com/runpod/tetra-rp/commit/3fe4cf02472e6cc19cd46a8104b248e2e7367be3)) -* separate sigstore artifacts from dist/ to avoid PyPI validation errors ([6b0038e](https://github.com/runpod/tetra-rp/commit/6b0038ea1c1df058263d2395248fb96d4c88cdeb)) +* revert to simple working CI/release workflows, remove broken shared workflow ([3fe4cf0](https://github.com/runpod/runpod-flash/commit/3fe4cf02472e6cc19cd46a8104b248e2e7367be3)) +* separate sigstore artifacts from dist/ to avoid PyPI validation errors ([6b0038e](https://github.com/runpod/runpod-flash/commit/6b0038ea1c1df058263d2395248fb96d4c88cdeb)) ### Documentation -* add contributing section to README with release system documentation reference ([8b87b9c](https://github.com/runpod/tetra-rp/commit/8b87b9cb09262f5be74a8f18435d685b0360d1c5)) +* add contributing section to README with release system documentation reference ([8b87b9c](https://github.com/runpod/runpod-flash/commit/8b87b9cb09262f5be74a8f18435d685b0360d1c5)) -## [0.5.3](https://github.com/runpod/tetra-rp/compare/0.5.2...0.5.3) (2025-07-10) +## [0.5.3](https://github.com/runpod/runpod-flash/compare/0.5.2...0.5.3) (2025-07-10) ### Bug Fixes -* update sigstore action to v3.0.0 to avoid deprecated upload-artifact@v3 ([92a6891](https://github.com/runpod/tetra-rp/commit/92a6891e26d327b2c99926373822e0eede08eebb)) +* update sigstore action to v3.0.0 to avoid deprecated upload-artifact@v3 ([92a6891](https://github.com/runpod/runpod-flash/commit/92a6891e26d327b2c99926373822e0eede08eebb)) -## [0.5.2](https://github.com/runpod/tetra-rp/compare/0.5.1...0.5.2) (2025-07-10) +## [0.5.2](https://github.com/runpod/runpod-flash/compare/0.5.1...0.5.2) (2025-07-10) ### Bug Fixes -* revert to simple working CI/release workflows, remove broken shared workflow ([62dab19](https://github.com/runpod/tetra-rp/commit/62dab1937be0d6c918261299e3be197cda0f44a9)) +* revert to simple working CI/release workflows, remove broken shared workflow ([62dab19](https://github.com/runpod/runpod-flash/commit/62dab1937be0d6c918261299e3be197cda0f44a9)) -## [0.5.1](https://github.com/runpod/tetra-rp/compare/0.5.0...0.5.1) (2025-07-10) +## [0.5.1](https://github.com/runpod/runpod-flash/compare/0.5.0...0.5.1) (2025-07-10) ### Bug Fixes -* adjust release-please title pattern to match existing PR [#55](https://github.com/runpod/tetra-rp/issues/55) ([091dbff](https://github.com/runpod/tetra-rp/commit/091dbffe8b26142eeedc07e1855d132e948cc3cd)) -* only upload artifacts to release when tag_name is available ([e8bdcf1](https://github.com/runpod/tetra-rp/commit/e8bdcf12cf7aeb57c04232aea2f14174c743481b)) -* revert to clean release-please title pattern for future releases ([c0171ca](https://github.com/runpod/tetra-rp/commit/c0171cab08f0ea62a861c1a0b8012e1c4dbd89c7)) -* still seeing issues with the CI and Release ([#60](https://github.com/runpod/tetra-rp/issues/60)) ([81df31a](https://github.com/runpod/tetra-rp/commit/81df31a0853c6436964afeb2511499396c66fe0d)) +* adjust release-please title pattern to match existing PR [#55](https://github.com/runpod/runpod-flash/issues/55) ([091dbff](https://github.com/runpod/runpod-flash/commit/091dbffe8b26142eeedc07e1855d132e948cc3cd)) +* only upload artifacts to release when tag_name is available ([e8bdcf1](https://github.com/runpod/runpod-flash/commit/e8bdcf12cf7aeb57c04232aea2f14174c743481b)) +* revert to clean release-please title pattern for future releases ([c0171ca](https://github.com/runpod/runpod-flash/commit/c0171cab08f0ea62a861c1a0b8012e1c4dbd89c7)) +* still seeing issues with the CI and Release ([#60](https://github.com/runpod/runpod-flash/issues/60)) ([81df31a](https://github.com/runpod/runpod-flash/commit/81df31a0853c6436964afeb2511499396c66fe0d)) -## [0.5.0](https://github.com/runpod/tetra-rp/compare/v0.4.2...0.5.0) (2025-07-09) +## [0.5.0](https://github.com/runpod/runpod-flash/compare/v0.4.2...0.5.0) (2025-07-09) ### Features -* pytest with unit and integration ([19dcac9](https://github.com/runpod/tetra-rp/commit/19dcac908ddd5f2cdac7d9365c3b2066fbe04fdd)) +* pytest with unit and integration ([19dcac9](https://github.com/runpod/runpod-flash/commit/19dcac908ddd5f2cdac7d9365c3b2066fbe04fdd)) ### Bug Fixes -* cannot do both ([c64d895](https://github.com/runpod/tetra-rp/commit/c64d89550f871b5fb685adc6dcb913cbd61217e4)) -* configure release-please to handle all commit types ([#57](https://github.com/runpod/tetra-rp/issues/57)) ([7de5c8d](https://github.com/runpod/tetra-rp/commit/7de5c8d146eaf2fddde07bc9c5ecf260e37029e3)) -* correction ([9982236](https://github.com/runpod/tetra-rp/commit/99822362fd99698a30ddcaab6bbd5513ed5549d8)) -* Pydantic's deprecation warning of class Config ([d1f18f2](https://github.com/runpod/tetra-rp/commit/d1f18f207568866aadb487ca99747b542174142b)) -* ruff cleanup ([8251597](https://github.com/runpod/tetra-rp/commit/825159748fdb8755aeee03d1740081915fd0882f)) -* ruff linting ([6d8e87c](https://github.com/runpod/tetra-rp/commit/6d8e87cb095b8d38977c5b3bfe981c8ebe580c93)) +* cannot do both ([c64d895](https://github.com/runpod/runpod-flash/commit/c64d89550f871b5fb685adc6dcb913cbd61217e4)) +* configure release-please to handle all commit types ([#57](https://github.com/runpod/runpod-flash/issues/57)) ([7de5c8d](https://github.com/runpod/runpod-flash/commit/7de5c8d146eaf2fddde07bc9c5ecf260e37029e3)) +* correction ([9982236](https://github.com/runpod/runpod-flash/commit/99822362fd99698a30ddcaab6bbd5513ed5549d8)) +* Pydantic's deprecation warning of class Config ([d1f18f2](https://github.com/runpod/runpod-flash/commit/d1f18f207568866aadb487ca99747b542174142b)) +* ruff cleanup ([8251597](https://github.com/runpod/runpod-flash/commit/825159748fdb8755aeee03d1740081915fd0882f)) +* ruff linting ([6d8e87c](https://github.com/runpod/runpod-flash/commit/6d8e87cb095b8d38977c5b3bfe981c8ebe580c93)) ### Documentation -* pull request template ([#58](https://github.com/runpod/tetra-rp/issues/58)) ([0377fa4](https://github.com/runpod/tetra-rp/commit/0377fa4dfc439e56df30227a03cf77bb80dfa28a)) +* pull request template ([#58](https://github.com/runpod/runpod-flash/issues/58)) ([0377fa4](https://github.com/runpod/runpod-flash/commit/0377fa4dfc439e56df30227a03cf77bb80dfa28a)) -## [0.4.2](https://github.com/runpod/tetra-rp/compare/v0.4.1...v0.4.2) (2025-06-26) +## [0.4.2](https://github.com/runpod/runpod-flash/compare/v0.4.1...v0.4.2) (2025-06-26) ### Bug Fixes -* consolidate PodTemplate overrides with the defaults per ServerlessResource ([2b8dc16](https://github.com/runpod/tetra-rp/commit/2b8dc165c46ce5d70651f7060075815977034557)) -* template overrides ([0fd6429](https://github.com/runpod/tetra-rp/commit/0fd6429ee1ad5ade9df5bfc4bbbdc9f29683b75b)) +* consolidate PodTemplate overrides with the defaults per ServerlessResource ([2b8dc16](https://github.com/runpod/runpod-flash/commit/2b8dc165c46ce5d70651f7060075815977034557)) +* template overrides ([0fd6429](https://github.com/runpod/runpod-flash/commit/0fd6429ee1ad5ade9df5bfc4bbbdc9f29683b75b)) -## [0.4.1](https://github.com/runpod/tetra-rp/compare/v0.4.0...v0.4.1) (2025-06-26) +## [0.4.1](https://github.com/runpod/runpod-flash/compare/v0.4.0...v0.4.1) (2025-06-26) ### Bug Fixes -* uv publish ([6c4f9a9](https://github.com/runpod/tetra-rp/commit/6c4f9a9d4deeffc4733798208e771cd136ab2e80)) -* uv publish ([55ae7c0](https://github.com/runpod/tetra-rp/commit/55ae7c08d70e079bb0abe2938b0d13771de3dce1)) +* uv publish ([6c4f9a9](https://github.com/runpod/runpod-flash/commit/6c4f9a9d4deeffc4733798208e771cd136ab2e80)) +* uv publish ([55ae7c0](https://github.com/runpod/runpod-flash/commit/55ae7c08d70e079bb0abe2938b0d13771de3dce1)) -## [0.4.0](https://github.com/runpod/tetra-rp/compare/v0.3.0...v0.4.0) (2025-06-26) +## [0.4.0](https://github.com/runpod/runpod-flash/compare/v0.3.0...v0.4.0) (2025-06-26) ### Features -* CPU Endpoints and Live Serverless ([c335fd1](https://github.com/runpod/tetra-rp/commit/c335fd11cbc65c4a18edabe007982506bee5e6c3)) +* CPU Endpoints and Live Serverless ([c335fd1](https://github.com/runpod/runpod-flash/commit/c335fd11cbc65c4a18edabe007982506bee5e6c3)) ### Bug Fixes -* CpuInstanceType values ([9cf958b](https://github.com/runpod/tetra-rp/commit/9cf958b381d56b4aa5d42c0393500818d6de3c34)) +* CpuInstanceType values ([9cf958b](https://github.com/runpod/runpod-flash/commit/9cf958b381d56b4aa5d42c0393500818d6de3c34)) -## [0.3.0](https://github.com/runpod/tetra-rp/compare/v0.2.1...v0.3.0) (2025-06-23) +## [0.3.0](https://github.com/runpod/runpod-flash/compare/v0.2.1...v0.3.0) (2025-06-23) ### Features -* added diffusers as examples group dependency ([e330bf9](https://github.com/runpod/tetra-rp/commit/e330bf9ed2305470543dd39470c1803959479821)) -* AE-392: serverless execute function calls runpod SDK ([43900e5](https://github.com/runpod/tetra-rp/commit/43900e59e3c4e08b5a903751ce5525fcfee93f13)) -* AE-394: examples updated ([27c3afb](https://github.com/runpod/tetra-rp/commit/27c3afbb93667d7a800af0f3e49361cb5b806070)) -* AE-394: examples updated ([d2bfe1b](https://github.com/runpod/tetra-rp/commit/d2bfe1b8c8682655a62148fcc3d0f0041a2f35d4)) -* AE-394: LiveServerless that locks the template id ([d1032a6](https://github.com/runpod/tetra-rp/commit/d1032a6b2788a18eb19eee991134fb8152733a11)) -* AE-394: LiveServerless that locks the template id ([1ba5bfc](https://github.com/runpod/tetra-rp/commit/1ba5bfce5d597a040ad66358dad2a86b0a66c3d6)) -* AE-394: resilient serverless calls with retries and auto-cancellations ([78bb250](https://github.com/runpod/tetra-rp/commit/78bb2505b7f7e02c82a2fe54ca1643de73affb54)) -* AE-394: retry, backoff, on cancel appropriately ([4b75941](https://github.com/runpod/tetra-rp/commit/4b759419f322f1dac87b4fd65d19430928ff9144)) -* AE-394: retry, backoff, on cancel appropriately ([96d1001](https://github.com/runpod/tetra-rp/commit/96d1001915654d3f101ac32bde972f17a0a3ff22)) -* AE-432: Logging ([a93f88e](https://github.com/runpod/tetra-rp/commit/a93f88e20a2fb0f6d741aebe98db85cc8b3ed88b)) -* AE-432: Logging ([718de18](https://github.com/runpod/tetra-rp/commit/718de18289b2d605ecc01359c6be9b6290373d3a)) -* AE-442: Docker image template polish + cleanup ([75471b2](https://github.com/runpod/tetra-rp/commit/75471b2cbb40d31d0d82d4562072d8c16a59cb8e)) -* AE-470: shadow deploy as tetra_rp ([749d427](https://github.com/runpod/tetra-rp/commit/749d427b9095d77742ddde679d1fc44d999d5d30)) -* AE-494: remove all docker- worker-related content ([72a4b9a](https://github.com/runpod/tetra-rp/commit/72a4b9a74761affdb1b0e9381bcaf49763829bac)) -* AE-494: removed any worker-related content ([72464d1](https://github.com/runpod/tetra-rp/commit/72464d1881584a2003d2edbd2cf9e6a982455592)) -* AE-517: logs from remote printed as `Remote | ` ([d8f4ee1](https://github.com/runpod/tetra-rp/commit/d8f4ee1323696b3f9cd8e5c2e928eb8538c6d2cd)) -* AE-517: logs from remote printed as `Remote | ` ([ab1d729](https://github.com/runpod/tetra-rp/commit/ab1d72900e21cccebd5448f63220295f113aeeb1)) -* AE-517: remote error logs are piped back ([b668e3e](https://github.com/runpod/tetra-rp/commit/b668e3ef0d97babebfaf35f31d053be8fbee7ad9)) -* AE-517: remote error logs are piped back ([4c5bf4c](https://github.com/runpod/tetra-rp/commit/4c5bf4c03d85a55a10739ea0911cff7d257ffe1b)) -* AE-565 singledispatch stub_resource for LiveServerless ([5fe2de7](https://github.com/runpod/tetra-rp/commit/5fe2de7cf74fc39ced6699aeb6cf4f48af21f3e9)) -* incremental changes, starting with deployments ([1ce1b3b](https://github.com/runpod/tetra-rp/commit/1ce1b3b04c2fdff2bfb870c6b9065bd6213a506d)) -* is_deployed() to check and redeploy in case endpoint is inaccessible ([4aa0932](https://github.com/runpod/tetra-rp/commit/4aa0932c9f99b08a0d234d4d843dd6339cbc7dd1)) -* Prefer A40 for GPU list when "any" is set ([6a60418](https://github.com/runpod/tetra-rp/commit/6a60418452a39501b3142096e9ab8afa01628953)) -* preparing json.dump of complex objects with normalize_for_json ([48cedd6](https://github.com/runpod/tetra-rp/commit/48cedd64e9140d261f2b3327a3611715d2bf3e38)) -* Pydantic modeling of Runpod resources ([87067bb](https://github.com/runpod/tetra-rp/commit/87067bbae31d7de618d0edd1d78289fef1c0f6d1)) -* remote stub for ServerlessEndpoint calls ([739aa93](https://github.com/runpod/tetra-rp/commit/739aa93775e296fbb99aa9afccc07b12ae60ea50)) -* resilient redeployments ([fd2e78d](https://github.com/runpod/tetra-rp/commit/fd2e78d42531f83cc3b66d197fb2195244d65336)) -* resilient serverless calls with retries and auto-cancellations when unhealthy/throttled ([9f53e8c](https://github.com/runpod/tetra-rp/commit/9f53e8cb36871ff58b491cd8bf1851bf3d1029cc)) -* resource utility, `inquire` to map runpod calls to pydantic models ([096239f](https://github.com/runpod/tetra-rp/commit/096239fae42dbcfafb141b48f294942e0e840990)) -* ServerlessEndpoint inherits ServerlessResource ([c0e64f9](https://github.com/runpod/tetra-rp/commit/c0e64f930bf38f2a956463d74f6bccfafee13353)) -* ServerlessResource.run_sync ([33e3fff](https://github.com/runpod/tetra-rp/commit/33e3fffeb121977c14614f0f0d57cfb232756e8c)) -* SingletonMixin for ResourceManager and RemoteExecutionClient ([e0ae91b](https://github.com/runpod/tetra-rp/commit/e0ae91b0627226873b131abdb26ab6d86e88e4d2)) -* Support for ServerlessEndpoint remote calls ([e53f3aa](https://github.com/runpod/tetra-rp/commit/e53f3aa4ef44f6e8a87d2d37fe7ae9b5f10e29f6)) +* added diffusers as examples group dependency ([e330bf9](https://github.com/runpod/runpod-flash/commit/e330bf9ed2305470543dd39470c1803959479821)) +* AE-392: serverless execute function calls runpod SDK ([43900e5](https://github.com/runpod/runpod-flash/commit/43900e59e3c4e08b5a903751ce5525fcfee93f13)) +* AE-394: examples updated ([27c3afb](https://github.com/runpod/runpod-flash/commit/27c3afbb93667d7a800af0f3e49361cb5b806070)) +* AE-394: examples updated ([d2bfe1b](https://github.com/runpod/runpod-flash/commit/d2bfe1b8c8682655a62148fcc3d0f0041a2f35d4)) +* AE-394: LiveServerless that locks the template id ([d1032a6](https://github.com/runpod/runpod-flash/commit/d1032a6b2788a18eb19eee991134fb8152733a11)) +* AE-394: LiveServerless that locks the template id ([1ba5bfc](https://github.com/runpod/runpod-flash/commit/1ba5bfce5d597a040ad66358dad2a86b0a66c3d6)) +* AE-394: resilient serverless calls with retries and auto-cancellations ([78bb250](https://github.com/runpod/runpod-flash/commit/78bb2505b7f7e02c82a2fe54ca1643de73affb54)) +* AE-394: retry, backoff, on cancel appropriately ([4b75941](https://github.com/runpod/runpod-flash/commit/4b759419f322f1dac87b4fd65d19430928ff9144)) +* AE-394: retry, backoff, on cancel appropriately ([96d1001](https://github.com/runpod/runpod-flash/commit/96d1001915654d3f101ac32bde972f17a0a3ff22)) +* AE-432: Logging ([a93f88e](https://github.com/runpod/runpod-flash/commit/a93f88e20a2fb0f6d741aebe98db85cc8b3ed88b)) +* AE-432: Logging ([718de18](https://github.com/runpod/runpod-flash/commit/718de18289b2d605ecc01359c6be9b6290373d3a)) +* AE-442: Docker image template polish + cleanup ([75471b2](https://github.com/runpod/runpod-flash/commit/75471b2cbb40d31d0d82d4562072d8c16a59cb8e)) +* AE-470: shadow deploy as runpod_flash ([749d427](https://github.com/runpod/runpod-flash/commit/749d427b9095d77742ddde679d1fc44d999d5d30)) +* AE-494: remove all docker- worker-related content ([72a4b9a](https://github.com/runpod/runpod-flash/commit/72a4b9a74761affdb1b0e9381bcaf49763829bac)) +* AE-494: removed any worker-related content ([72464d1](https://github.com/runpod/runpod-flash/commit/72464d1881584a2003d2edbd2cf9e6a982455592)) +* AE-517: logs from remote printed as `Remote | ` ([d8f4ee1](https://github.com/runpod/runpod-flash/commit/d8f4ee1323696b3f9cd8e5c2e928eb8538c6d2cd)) +* AE-517: logs from remote printed as `Remote | ` ([ab1d729](https://github.com/runpod/runpod-flash/commit/ab1d72900e21cccebd5448f63220295f113aeeb1)) +* AE-517: remote error logs are piped back ([b668e3e](https://github.com/runpod/runpod-flash/commit/b668e3ef0d97babebfaf35f31d053be8fbee7ad9)) +* AE-517: remote error logs are piped back ([4c5bf4c](https://github.com/runpod/runpod-flash/commit/4c5bf4c03d85a55a10739ea0911cff7d257ffe1b)) +* AE-565 singledispatch stub_resource for LiveServerless ([5fe2de7](https://github.com/runpod/runpod-flash/commit/5fe2de7cf74fc39ced6699aeb6cf4f48af21f3e9)) +* incremental changes, starting with deployments ([1ce1b3b](https://github.com/runpod/runpod-flash/commit/1ce1b3b04c2fdff2bfb870c6b9065bd6213a506d)) +* is_deployed() to check and redeploy in case endpoint is inaccessible ([4aa0932](https://github.com/runpod/runpod-flash/commit/4aa0932c9f99b08a0d234d4d843dd6339cbc7dd1)) +* Prefer A40 for GPU list when "any" is set ([6a60418](https://github.com/runpod/runpod-flash/commit/6a60418452a39501b3142096e9ab8afa01628953)) +* preparing json.dump of complex objects with normalize_for_json ([48cedd6](https://github.com/runpod/runpod-flash/commit/48cedd64e9140d261f2b3327a3611715d2bf3e38)) +* Pydantic modeling of Runpod resources ([87067bb](https://github.com/runpod/runpod-flash/commit/87067bbae31d7de618d0edd1d78289fef1c0f6d1)) +* remote stub for ServerlessEndpoint calls ([739aa93](https://github.com/runpod/runpod-flash/commit/739aa93775e296fbb99aa9afccc07b12ae60ea50)) +* resilient redeployments ([fd2e78d](https://github.com/runpod/runpod-flash/commit/fd2e78d42531f83cc3b66d197fb2195244d65336)) +* resilient serverless calls with retries and auto-cancellations when unhealthy/throttled ([9f53e8c](https://github.com/runpod/runpod-flash/commit/9f53e8cb36871ff58b491cd8bf1851bf3d1029cc)) +* resource utility, `inquire` to map runpod calls to pydantic models ([096239f](https://github.com/runpod/runpod-flash/commit/096239fae42dbcfafb141b48f294942e0e840990)) +* ServerlessEndpoint inherits ServerlessResource ([c0e64f9](https://github.com/runpod/runpod-flash/commit/c0e64f930bf38f2a956463d74f6bccfafee13353)) +* ServerlessResource.run_sync ([33e3fff](https://github.com/runpod/runpod-flash/commit/33e3fffeb121977c14614f0f0d57cfb232756e8c)) +* SingletonMixin for ResourceManager and RemoteExecutionClient ([e0ae91b](https://github.com/runpod/runpod-flash/commit/e0ae91b0627226873b131abdb26ab6d86e88e4d2)) +* Support for ServerlessEndpoint remote calls ([e53f3aa](https://github.com/runpod/runpod-flash/commit/e53f3aa4ef44f6e8a87d2d37fe7ae9b5f10e29f6)) ### Bug Fixes -* add missing arg to request ([21d4e0c](https://github.com/runpod/tetra-rp/commit/21d4e0ceb6e255a5d5df1440c8fcbe95a75f802c)) -* add missing arg to request ([656150f](https://github.com/runpod/tetra-rp/commit/656150fc082b7b519d4b6c940258af8ac0b62e04)) -* AE-392: deployable serverless corrections ([57b1cd2](https://github.com/runpod/tetra-rp/commit/57b1cd21fa63bd9d11447cdba9a865ca1a3aeb8b)) -* AE-392: deployable serverless corrections ([74bd17d](https://github.com/runpod/tetra-rp/commit/74bd17d0dbf7bd24ddf3de3734405615a12af1a8)) -* cleanup from ruff suggestions ([61048d2](https://github.com/runpod/tetra-rp/commit/61048d2d7922ad53289fa078f9e48ac8b60dada7)) -* cleanup project setup ([9ed50ab](https://github.com/runpod/tetra-rp/commit/9ed50ab925e465ffeb8cedbffbb44036bdfb35c8)) -* cleanup unused code ([ad78de7](https://github.com/runpod/tetra-rp/commit/ad78de7942587dbb0100a3101a5185ebc1c0cc52)) -* implicit load_dotenv() before everything ([ff0030b](https://github.com/runpod/tetra-rp/commit/ff0030bd7c033d4a8e6b9849c76075bccb99beaa)) -* implicit load_dotenv() before everything ([2d3c710](https://github.com/runpod/tetra-rp/commit/2d3c71015a944515fa667aaa662692f14596b5e0)) -* make examples ([7de342c](https://github.com/runpod/tetra-rp/commit/7de342cafab59bffe764833d5a1f02b7ff691654)) -* missed these commits ([5ed3a8a](https://github.com/runpod/tetra-rp/commit/5ed3a8a4bc6065eb1c4313595f325581666d33ae)) -* passing GPU names aren't supported yet ([3798ac0](https://github.com/runpod/tetra-rp/commit/3798ac06ce1de21b711f5a7236ebaa884ffc22ae)) -* Put A40s in front of the list when gpus=any ([717efb1](https://github.com/runpod/tetra-rp/commit/717efb168059826ce2857c3be3d476a43e0eddfb)) -* python 3.9 compatible ([5ddfd09](https://github.com/runpod/tetra-rp/commit/5ddfd09d2fba9403aec9940eec479db5f259c1d8)) -* release-please bugs ([fd6d02e](https://github.com/runpod/tetra-rp/commit/fd6d02eb2a625082e2f31394577debc5c066b0f4)) -* release-please bugs ([76b3eee](https://github.com/runpod/tetra-rp/commit/76b3eee81d5a7e9a4eca1b06b0cdc6256d4d9743)) -* ruff cleanup ([739adaf](https://github.com/runpod/tetra-rp/commit/739adaff98f816dfb95f6b46887ba80b6e8e2aaa)) -* simplify `make dev` that syncs and installs examples ([d1a3e62](https://github.com/runpod/tetra-rp/commit/d1a3e62174b066330591a96cc98d7b7e1238f221)) -* try catch for matrix example ([f7a5012](https://github.com/runpod/tetra-rp/commit/f7a5012a1f177e96e3565ec86638e2712846bb71)) -* use of pathlib.Path where appropriate ([4ee717c](https://github.com/runpod/tetra-rp/commit/4ee717ca853ce88598c60db0cc3770b1f689d0ce)) - -## [0.2.1](https://github.com/runpod/tetra-rp/compare/0.2.0...v0.2.1) (2025-06-23) +* add missing arg to request ([21d4e0c](https://github.com/runpod/runpod-flash/commit/21d4e0ceb6e255a5d5df1440c8fcbe95a75f802c)) +* add missing arg to request ([656150f](https://github.com/runpod/runpod-flash/commit/656150fc082b7b519d4b6c940258af8ac0b62e04)) +* AE-392: deployable serverless corrections ([57b1cd2](https://github.com/runpod/runpod-flash/commit/57b1cd21fa63bd9d11447cdba9a865ca1a3aeb8b)) +* AE-392: deployable serverless corrections ([74bd17d](https://github.com/runpod/runpod-flash/commit/74bd17d0dbf7bd24ddf3de3734405615a12af1a8)) +* cleanup from ruff suggestions ([61048d2](https://github.com/runpod/runpod-flash/commit/61048d2d7922ad53289fa078f9e48ac8b60dada7)) +* cleanup project setup ([9ed50ab](https://github.com/runpod/runpod-flash/commit/9ed50ab925e465ffeb8cedbffbb44036bdfb35c8)) +* cleanup unused code ([ad78de7](https://github.com/runpod/runpod-flash/commit/ad78de7942587dbb0100a3101a5185ebc1c0cc52)) +* implicit load_dotenv() before everything ([ff0030b](https://github.com/runpod/runpod-flash/commit/ff0030bd7c033d4a8e6b9849c76075bccb99beaa)) +* implicit load_dotenv() before everything ([2d3c710](https://github.com/runpod/runpod-flash/commit/2d3c71015a944515fa667aaa662692f14596b5e0)) +* make examples ([7de342c](https://github.com/runpod/runpod-flash/commit/7de342cafab59bffe764833d5a1f02b7ff691654)) +* missed these commits ([5ed3a8a](https://github.com/runpod/runpod-flash/commit/5ed3a8a4bc6065eb1c4313595f325581666d33ae)) +* passing GPU names aren't supported yet ([3798ac0](https://github.com/runpod/runpod-flash/commit/3798ac06ce1de21b711f5a7236ebaa884ffc22ae)) +* Put A40s in front of the list when gpus=any ([717efb1](https://github.com/runpod/runpod-flash/commit/717efb168059826ce2857c3be3d476a43e0eddfb)) +* python 3.9 compatible ([5ddfd09](https://github.com/runpod/runpod-flash/commit/5ddfd09d2fba9403aec9940eec479db5f259c1d8)) +* release-please bugs ([fd6d02e](https://github.com/runpod/runpod-flash/commit/fd6d02eb2a625082e2f31394577debc5c066b0f4)) +* release-please bugs ([76b3eee](https://github.com/runpod/runpod-flash/commit/76b3eee81d5a7e9a4eca1b06b0cdc6256d4d9743)) +* ruff cleanup ([739adaf](https://github.com/runpod/runpod-flash/commit/739adaff98f816dfb95f6b46887ba80b6e8e2aaa)) +* simplify `make dev` that syncs and installs examples ([d1a3e62](https://github.com/runpod/runpod-flash/commit/d1a3e62174b066330591a96cc98d7b7e1238f221)) +* try catch for matrix example ([f7a5012](https://github.com/runpod/runpod-flash/commit/f7a5012a1f177e96e3565ec86638e2712846bb71)) +* use of pathlib.Path where appropriate ([4ee717c](https://github.com/runpod/runpod-flash/commit/4ee717ca853ce88598c60db0cc3770b1f689d0ce)) + +## [0.2.1](https://github.com/runpod/runpod-flash/compare/0.2.0...v0.2.1) (2025-06-23) ### Bug Fixes -* implicit load_dotenv() before everything ([2d3c710](https://github.com/runpod/tetra-rp/commit/2d3c71015a944515fa667aaa662692f14596b5e0)) +* implicit load_dotenv() before everything ([2d3c710](https://github.com/runpod/runpod-flash/commit/2d3c71015a944515fa667aaa662692f14596b5e0)) diff --git a/CLAUDE.md b/CLAUDE.md index 7265ea50..d599c325 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,13 +1,13 @@ -# tetra-rp Project Configuration +# runpod-flash Project Configuration ## Claude Code Configuration -When using Claude Code on this project, always prefer the tetra-code-intel MCP tools for code exploration instead of using Explore agents or generic search: +When using Claude Code on this project, always prefer the flash-code-intel MCP tools for code exploration instead of using Explore agents or generic search: -- `mcp__tetra-code-intel__find_symbol` - Search for classes, functions, methods by name -- `mcp__tetra-code-intel__get_class_interface` - Inspect class methods and properties -- `mcp__tetra-code-intel__list_file_symbols` - View file structure without reading full content -- `mcp__tetra-code-intel__list_classes` - Explore the class hierarchy -- `mcp__tetra-code-intel__find_by_decorator` - Find decorated items (e.g., `@property`, `@remote`) +- `mcp__flash-code-intel__find_symbol` - Search for classes, functions, methods by name +- `mcp__flash-code-intel__get_class_interface` - Inspect class methods and properties +- `mcp__flash-code-intel__list_file_symbols` - View file structure without reading full content +- `mcp__flash-code-intel__list_classes` - Explore the class hierarchy +- `mcp__flash-code-intel__find_by_decorator` - Find decorated items (e.g., `@property`, `@remote`) **Do NOT** use the Task tool with the "Explore" subagent for codebase exploration. Use the MCP tools directly instead. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fda9aef2..e9d9080a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contributing to tetra-rp +# Contributing to runpod-flash ## Development Setup @@ -12,8 +12,8 @@ ```bash # Clone the repository -git clone https://github.com/your-org/tetra-rp.git -cd tetra-rp +git clone https://github.com/your-org/runpod-flash.git +cd runpod-flash # Install dependencies and package in editable mode make dev @@ -278,7 +278,7 @@ make quality-check **Coverage threshold failures** - Use `--no-cov` flag: `uv run pytest tests/unit/test_file.py -v --no-cov` -- Or test specific module: `uv run pytest --cov=src/tetra_rp/module` +- Or test specific module: `uv run pytest --cov=src/runpod_flash/module` ## Release Process @@ -310,7 +310,7 @@ When reviewing code, consider: ## Getting Help -- Check existing [Issues](https://github.com/your-org/tetra-rp/issues) +- Check existing [Issues](https://github.com/your-org/runpod-flash/issues) - Review [README.md](README.md) for usage examples - See [TESTING.md](TESTING.md) for testing details - See [RELEASE_SYSTEM.md](RELEASE_SYSTEM.md) for release process @@ -354,7 +354,7 @@ uv run python scripts/code_intel.py interface LiveServerless **List symbols in a file:** ```bash -uv run python scripts/code_intel.py file tetra_rp/decorators.py +uv run python scripts/code_intel.py file runpod_flash/decorators.py ``` **List all symbols:** @@ -373,7 +373,7 @@ Claude Code automatically uses the MCP code intelligence server when exploring t - **85% token reduction**: No need to read full files for structure queries - **Instant results**: Direct database queries instead of file parsing -The MCP server is configured in `.mcp.json` and automatically activated when you open this project in Claude Code. Use the `/tetra-explorer` skill to get guidance on best exploration practices. +The MCP server is configured in `.mcp.json` and automatically activated when you open this project in Claude Code. Use the `/flash-explorer` skill to get guidance on best exploration practices. Available MCP tools: - `find_symbol` - Search for classes, functions, methods @@ -405,7 +405,7 @@ uv run python scripts/code_intel.py interface ```bash # Instead of reading full file (500+ tokens): # Do this query first (50 tokens): -uv run python scripts/code_intel.py file tetra_rp/decorators.py +uv run python scripts/code_intel.py file runpod_flash/decorators.py # Then only read full file if implementation details needed ``` diff --git a/Makefile b/Makefile index 251de4a3..ff9cb101 100644 --- a/Makefile +++ b/Makefile @@ -73,11 +73,11 @@ test-integration-serial: # Run integration tests serially (for debugging) uv run pytest tests/integration/ -v -m integration test-coverage: # Run tests with coverage report (parallel by default) - uv run pytest tests/ -v -n auto -m "not serial" --cov=tetra_rp --cov-report=xml - uv run pytest tests/ -v -m "serial" --cov=tetra_rp --cov-append --cov-report=term-missing + uv run pytest tests/ -v -n auto -m "not serial" --cov=runpod_flash --cov-report=xml + uv run pytest tests/ -v -m "serial" --cov=runpod_flash --cov-append --cov-report=term-missing test-coverage-serial: # Run tests with coverage report (serial execution) - uv run pytest tests/ -v --cov=tetra_rp --cov-report=term-missing + uv run pytest tests/ -v --cov=runpod_flash --cov-report=term-missing test-fast: # Run tests with fast-fail mode and parallel execution uv run pytest tests/ -v -x --tb=short -n auto @@ -116,8 +116,8 @@ ci-quality-github: # Quality checks with GitHub Actions formatting (parallel by uv run ruff check . --output-format=github @echo "::endgroup::" @echo "::group::Test suite with coverage" - uv run pytest tests/ --junitxml=pytest-results.xml -v -n auto -m "not serial" --cov=tetra_rp --cov-report=xml --cov-fail-under=0 - uv run pytest tests/ --junitxml=pytest-results.xml -v -m "serial" --cov=tetra_rp --cov-append --cov-report=term-missing + uv run pytest tests/ --junitxml=pytest-results.xml -v -n auto -m "not serial" --cov=runpod_flash --cov-report=xml --cov-fail-under=0 + uv run pytest tests/ --junitxml=pytest-results.xml -v -m "serial" --cov=runpod_flash --cov-append --cov-report=term-missing @echo "::endgroup::" ci-quality-github-serial: # Serial quality checks for GitHub Actions (for debugging) @@ -128,5 +128,5 @@ ci-quality-github-serial: # Serial quality checks for GitHub Actions (for debugg uv run ruff check . --output-format=github @echo "::endgroup::" @echo "::group::Test suite with coverage (serial)" - uv run pytest tests/ --junitxml=pytest-results.xml -v --cov=tetra_rp --cov-report=term-missing + uv run pytest tests/ --junitxml=pytest-results.xml -v --cov=runpod_flash --cov-report=term-missing @echo "::endgroup::" diff --git a/README.md b/README.md index 8273bad2..e05f059a 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,3 @@ -# ⚠️ DEPRECATED: tetra-rp - -> **This package is deprecated.** The final release of `tetra-rp` is version 0.25.2. All future development and releases will be under the new package name `runpod-flash`. -> -> **Migration Required:** To upgrade, install the new package and update your imports: -> ```bash -> pip uninstall tetra-rp -> pip install runpod-flash -> ``` -> Then update your imports from `from tetra_rp import ...` to `from runpod_flash import ...` -> -> See [runpod/flash](https://github.com/runpod/flash) for the new repository. - # Flash: Serverless computing for AI workloads Runpod Flash is a Python SDK that streamlines the development and deployment of AI workflows on Runpod's [Serverless infrastructure](http://docs.runpod.io/serverless/overview). Write Python functions locally, and Flash handles the infrastructure, provisioning GPUs and CPUs, managing dependencies, and transferring data, allowing you to focus on building AI applications. @@ -56,14 +43,8 @@ Before you can use Flash, you'll need: ### Step 1: Install Flash -> **Note:** This documentation describes the deprecated `tetra-rp` package. For new projects, use `runpod-flash`: -> ```bash -> pip install runpod-flash -> ``` - -For the legacy package: ```bash -pip install tetra_rp +pip install runpod-flash ``` ### Step 2: Set your API key @@ -86,7 +67,7 @@ Add the following code to a new Python file: ```python import asyncio -from tetra_rp import remote, LiveServerless +from runpod_flash import remote, LiveServerless from dotenv import load_dotenv # Uncomment if using a .env file @@ -310,7 +291,7 @@ async def main(): Flash provides fine-grained control over hardware allocation through configuration objects: ```python -from tetra_rp import LiveServerless, GpuGroup, CpuInstanceType, PodTemplate +from runpod_flash import LiveServerless, GpuGroup, CpuInstanceType, PodTemplate # GPU configuration gpu_config = LiveServerless( @@ -364,7 +345,7 @@ results = await asyncio.gather( For API endpoints requiring low-latency HTTP access with direct routing, use load-balanced endpoints: ```python -from tetra_rp import LiveLoadBalancer, remote +from runpod_flash import LiveLoadBalancer, remote api = LiveLoadBalancer(name="api-service") @@ -413,7 +394,7 @@ Flash orchestrates workflow execution through a sophisticated multi-step process `LiveServerless` resources use a fixed Docker image that's optimized for Flash runtime, and supports full remote code execution. For specialized environments that require a custom Docker image, use `ServerlessEndpoint` or `CpuServerlessEndpoint`: ```python -from tetra_rp import ServerlessEndpoint +from runpod_flash import ServerlessEndpoint custom_gpu = ServerlessEndpoint( name="custom-ml-env", @@ -525,7 +506,7 @@ For information on load-balanced endpoints (required for Mothership and HTTP ser RunPod serverless has a **500MB deployment limit**. Exceeding this limit will cause deployment failures. -Use `--exclude` to skip packages already in your worker-tetra Docker image: +Use `--exclude` to skip packages already in your worker-flash Docker image: ```bash # For GPU deployments (PyTorch pre-installed) @@ -539,7 +520,7 @@ flash build --exclude torch,torchvision,torchaudio - **CPU resources** → Python slim images have NO ML frameworks pre-installed - **Load-balanced** → Same as above, depends on GPU vs CPU variant -See [worker-tetra](https://github.com/runpod-workers/worker-tetra) for base image details. +See [worker-flash](https://github.com/runpod-workers/worker-flash) for base image details. ## Configuration @@ -614,7 +595,7 @@ Some common GPU groups available through `GpuGroup`: ```python import asyncio -from tetra_rp import remote, LiveServerless +from runpod_flash import remote, LiveServerless # Simple GPU configuration gpu_config = LiveServerless(name="example-gpu-server") @@ -653,7 +634,7 @@ if __name__ == "__main__": ```python import asyncio -from tetra_rp import remote, LiveServerless, GpuGroup, PodTemplate +from runpod_flash import remote, LiveServerless, GpuGroup, PodTemplate import base64 # Advanced GPU configuration with consolidated template overrides @@ -708,7 +689,7 @@ if __name__ == "__main__": ```python import asyncio -from tetra_rp import remote, LiveServerless, CpuInstanceType +from runpod_flash import remote, LiveServerless, CpuInstanceType # Simple CPU configuration cpu_config = LiveServerless( @@ -756,7 +737,7 @@ if __name__ == "__main__": ```python import asyncio import base64 -from tetra_rp import remote, LiveServerless, CpuInstanceType, PodTemplate +from runpod_flash import remote, LiveServerless, CpuInstanceType, PodTemplate # Advanced CPU configuration with template overrides data_processing_config = LiveServerless( @@ -833,7 +814,7 @@ if __name__ == "__main__": ```python import asyncio -from tetra_rp import remote, LiveServerless, GpuGroup, CpuInstanceType, PodTemplate +from runpod_flash import remote, LiveServerless, GpuGroup, CpuInstanceType, PodTemplate # GPU configuration for model inference gpu_config = LiveServerless( @@ -948,7 +929,7 @@ if __name__ == "__main__": ```python import os import asyncio -from tetra_rp import remote, LiveServerless +from runpod_flash import remote, LiveServerless # Configure Runpod resources runpod_config = LiveServerless(name="multi-stage-pipeline-server") @@ -1107,6 +1088,6 @@ def fetch_data(url): This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

- Flash • + FlashRunpod

diff --git a/RELEASE_SYSTEM.md b/RELEASE_SYSTEM.md index 450a9116..eb825bbb 100644 --- a/RELEASE_SYSTEM.md +++ b/RELEASE_SYSTEM.md @@ -2,7 +2,7 @@ ## Overview -The tetra-rp project uses a simple, reliable release automation system built on **Release Please v4** with quality gates and automated PyPI publishing via OIDC trusted publishing. +The runpod-flash project uses a simple, reliable release automation system built on **Release Please v4** with quality gates and automated PyPI publishing via OIDC trusted publishing. ## Architecture diff --git a/TESTING.md b/TESTING.md index ad87ee40..3e60f389 100644 --- a/TESTING.md +++ b/TESTING.md @@ -8,7 +8,7 @@ uv pip install -e . # Development (complete - includes packaging validation) # Build wheel and install it to test CLI from anywhere -cd /path/to/tetra-rp && uv build && pip install dist/tetra_rp-*.whl --force-reinstall +cd /path/to/runpod_flash && uv build && pip install dist/runpod_flash-*.whl --force-reinstall cd /tmp && flash init test_project # Unit tests @@ -38,11 +38,11 @@ make validate-wheel **Coverage threshold failures** - Use `--no-cov` flag for focused testing: `uv run pytest tests/unit/test_skeleton.py -v --no-cov` -- Or test specific module: `uv run pytest --cov=src/tetra_rp/cli/utils/skeleton` +- Or test specific module: `uv run pytest --cov=src/runpod_flash/cli/utils/skeleton` **Hidden files require explicit glob patterns** - Pattern `**/.*` needed in pyproject.toml to include `.env`, `.gitignore`, `.flashignore` -- Verify with: `unzip -l dist/tetra_rp-*.whl | grep skeleton_template` +- Verify with: `unzip -l dist/runpod_flash-*.whl | grep skeleton_template` ## Pre-Release Checklist diff --git a/VERIFICATION.md b/VERIFICATION.md index d36bbfe8..02def0ec 100644 --- a/VERIFICATION.md +++ b/VERIFICATION.md @@ -13,7 +13,7 @@ The fix centralizes all Docker image references into constants that support envi ### Run All Tests ```bash -cd /Users/deanquinanola/Github/python/tetra-rp +cd /Users/deanquinanola/Github/python/runpod-flash # Run the verification script uv run python3 scripts/test-image-constants.py @@ -41,48 +41,48 @@ uv run python3 << 'EOF' import sys sys.path.insert(0, 'src') -from tetra_rp.core.resources.constants import ( - TETRA_IMAGE_TAG, - TETRA_GPU_IMAGE, - TETRA_CPU_IMAGE, - TETRA_LB_IMAGE, - TETRA_CPU_LB_IMAGE, +from runpod_flash.core.resources.constants import ( + FLASH_IMAGE_TAG, + FLASH_GPU_IMAGE, + FLASH_CPU_IMAGE, + FLASH_LB_IMAGE, + FLASH_CPU_LB_IMAGE, DEFAULT_WORKERS_MIN, DEFAULT_WORKERS_MAX, ) -print(f"TETRA_IMAGE_TAG: {TETRA_IMAGE_TAG}") -print(f"TETRA_GPU_IMAGE: {TETRA_GPU_IMAGE}") -print(f"TETRA_CPU_IMAGE: {TETRA_CPU_IMAGE}") -print(f"TETRA_LB_IMAGE: {TETRA_LB_IMAGE}") -print(f"TETRA_CPU_LB_IMAGE: {TETRA_CPU_LB_IMAGE}") +print(f"FLASH_IMAGE_TAG: {FLASH_IMAGE_TAG}") +print(f"FLASH_GPU_IMAGE: {FLASH_GPU_IMAGE}") +print(f"FLASH_CPU_IMAGE: {FLASH_CPU_IMAGE}") +print(f"FLASH_LB_IMAGE: {FLASH_LB_IMAGE}") +print(f"FLASH_CPU_LB_IMAGE: {FLASH_CPU_LB_IMAGE}") print(f"DEFAULT_WORKERS_MIN: {DEFAULT_WORKERS_MIN}") print(f"DEFAULT_WORKERS_MAX: {DEFAULT_WORKERS_MAX}") EOF ``` -### Test 2: Environment Variable Override (TETRA_IMAGE_TAG=local) +### Test 2: Environment Variable Override (FLASH_IMAGE_TAG=local) ```bash -TETRA_IMAGE_TAG=local uv run python3 << 'EOF' +FLASH_IMAGE_TAG=local uv run python3 << 'EOF' import sys sys.path.insert(0, 'src') -from tetra_rp.core.resources.constants import ( - TETRA_IMAGE_TAG, - TETRA_GPU_IMAGE, - TETRA_LB_IMAGE, - TETRA_CPU_LB_IMAGE, +from runpod_flash.core.resources.constants import ( + FLASH_IMAGE_TAG, + FLASH_GPU_IMAGE, + FLASH_LB_IMAGE, + FLASH_CPU_LB_IMAGE, ) -print(f"With TETRA_IMAGE_TAG={TETRA_IMAGE_TAG}:") -print(f" TETRA_GPU_IMAGE: {TETRA_GPU_IMAGE}") -print(f" TETRA_LB_IMAGE: {TETRA_LB_IMAGE}") -print(f" TETRA_CPU_LB_IMAGE: {TETRA_CPU_LB_IMAGE}") +print(f"With FLASH_IMAGE_TAG={FLASH_IMAGE_TAG}:") +print(f" FLASH_GPU_IMAGE: {FLASH_GPU_IMAGE}") +print(f" FLASH_LB_IMAGE: {FLASH_LB_IMAGE}") +print(f" FLASH_CPU_LB_IMAGE: {FLASH_CPU_LB_IMAGE}") -assert ":local" in TETRA_GPU_IMAGE -assert ":local" in TETRA_LB_IMAGE -assert ":local" in TETRA_CPU_LB_IMAGE +assert ":local" in FLASH_GPU_IMAGE +assert ":local" in FLASH_LB_IMAGE +assert ":local" in FLASH_CPU_LB_IMAGE print("✓ All images use :local tag") EOF ``` @@ -90,14 +90,14 @@ EOF ### Test 3: Individual Image Override ```bash -TETRA_CPU_LB_IMAGE=custom/lb-cpu:v1 uv run python3 << 'EOF' +FLASH_CPU_LB_IMAGE=custom/lb-cpu:v1 uv run python3 << 'EOF' import sys sys.path.insert(0, 'src') -from tetra_rp.core.resources.constants import TETRA_CPU_LB_IMAGE +from runpod_flash.core.resources.constants import FLASH_CPU_LB_IMAGE -print(f"TETRA_CPU_LB_IMAGE: {TETRA_CPU_LB_IMAGE}") -assert TETRA_CPU_LB_IMAGE == "custom/lb-cpu:v1" +print(f"FLASH_CPU_LB_IMAGE: {FLASH_CPU_LB_IMAGE}") +assert FLASH_CPU_LB_IMAGE == "custom/lb-cpu:v1" print("✓ Custom override works") EOF ``` @@ -110,9 +110,9 @@ import sys sys.path.insert(0, 'src') from pathlib import Path -from tetra_rp.cli.commands.build_utils.manifest import ManifestBuilder -from tetra_rp.core.resources.constants import ( - TETRA_CPU_LB_IMAGE, +from runpod_flash.cli.commands.build_utils.manifest import ManifestBuilder +from runpod_flash.core.resources.constants import ( + FLASH_CPU_LB_IMAGE, DEFAULT_WORKERS_MIN, DEFAULT_WORKERS_MAX, ) @@ -124,11 +124,11 @@ mothership = builder._create_mothership_resource({ }) print(f"Mothership configuration:") -print(f" imageName: {mothership['imageName']} (expected: {TETRA_CPU_LB_IMAGE})") +print(f" imageName: {mothership['imageName']} (expected: {FLASH_CPU_LB_IMAGE})") print(f" workersMin: {mothership['workersMin']} (expected: {DEFAULT_WORKERS_MIN})") print(f" workersMax: {mothership['workersMax']} (expected: {DEFAULT_WORKERS_MAX})") -assert mothership['imageName'] == TETRA_CPU_LB_IMAGE +assert mothership['imageName'] == FLASH_CPU_LB_IMAGE assert mothership['workersMin'] == DEFAULT_WORKERS_MIN assert mothership['workersMax'] == DEFAULT_WORKERS_MAX @@ -143,11 +143,11 @@ uv run python3 << 'EOF' import sys sys.path.insert(0, 'src') -from tetra_rp import LiveServerless, LiveLoadBalancer, CpuLiveLoadBalancer -from tetra_rp.core.resources.constants import ( - TETRA_GPU_IMAGE, - TETRA_LB_IMAGE, - TETRA_CPU_LB_IMAGE, +from runpod_flash import LiveServerless, LiveLoadBalancer, CpuLiveLoadBalancer +from runpod_flash.core.resources.constants import ( + FLASH_GPU_IMAGE, + FLASH_LB_IMAGE, + FLASH_CPU_LB_IMAGE, ) gpu_ls = LiveServerless(name="test-gpu") @@ -155,13 +155,13 @@ gpu_lb = LiveLoadBalancer(name="test-gpu-lb") cpu_lb = CpuLiveLoadBalancer(name="test-cpu-lb") print(f"Resource image configuration:") -print(f" LiveServerless: {gpu_ls.imageName} (expected: {TETRA_GPU_IMAGE})") -print(f" LiveLoadBalancer: {gpu_lb.imageName} (expected: {TETRA_LB_IMAGE})") -print(f" CpuLiveLoadBalancer: {cpu_lb.imageName} (expected: {TETRA_CPU_LB_IMAGE})") +print(f" LiveServerless: {gpu_ls.imageName} (expected: {FLASH_GPU_IMAGE})") +print(f" LiveLoadBalancer: {gpu_lb.imageName} (expected: {FLASH_LB_IMAGE})") +print(f" CpuLiveLoadBalancer: {cpu_lb.imageName} (expected: {FLASH_CPU_LB_IMAGE})") -assert gpu_ls.imageName == TETRA_GPU_IMAGE -assert gpu_lb.imageName == TETRA_LB_IMAGE -assert cpu_lb.imageName == TETRA_CPU_LB_IMAGE +assert gpu_ls.imageName == FLASH_GPU_IMAGE +assert gpu_lb.imageName == FLASH_LB_IMAGE +assert cpu_lb.imageName == FLASH_CPU_LB_IMAGE print("✓ All LiveServerless classes use correct image constants") EOF @@ -171,10 +171,10 @@ EOF ```bash # Verify no hardcoded image names in manifest.py -grep -n "runpod/tetra-rp-lb" src/tetra_rp/cli/commands/build_utils/manifest.py || echo "✓ No hardcoded images found" +grep -n "runpod/runpod-flash-lb" src/runpod_flash/cli/commands/build_utils/manifest.py || echo "✓ No hardcoded images found" # Verify constants are imported -grep "TETRA_CPU_LB_IMAGE\|TETRA_LB_IMAGE\|DEFAULT_WORKERS" src/tetra_rp/cli/commands/build_utils/manifest.py +grep "FLASH_CPU_LB_IMAGE\|FLASH_LB_IMAGE\|DEFAULT_WORKERS" src/runpod_flash/cli/commands/build_utils/manifest.py ``` ### Test 7: Unit Tests Pass @@ -202,12 +202,12 @@ The verification tests cover: - Worker count constants used correctly 3. **LiveServerless Integration** (✓ 3 tests) - - `LiveServerless` uses `TETRA_GPU_IMAGE` - - `LiveLoadBalancer` uses `TETRA_LB_IMAGE` - - `CpuLiveLoadBalancer` uses `TETRA_CPU_LB_IMAGE` + - `LiveServerless` uses `FLASH_GPU_IMAGE` + - `LiveLoadBalancer` uses `FLASH_LB_IMAGE` + - `CpuLiveLoadBalancer` uses `FLASH_CPU_LB_IMAGE` 4. **Environment Variable Overrides** (✓ 1 test) - - `TETRA_IMAGE_TAG=dev` works correctly + - `FLASH_IMAGE_TAG=dev` works correctly - Individual image overrides work 5. **Code Quality** (✓ 6 tests) @@ -217,16 +217,16 @@ The verification tests cover: ## Environment Variables -### Global Override: TETRA_IMAGE_TAG +### Global Override: FLASH_IMAGE_TAG Affects all images at once: ```bash -export TETRA_IMAGE_TAG=local +export FLASH_IMAGE_TAG=local # or -export TETRA_IMAGE_TAG=dev +export FLASH_IMAGE_TAG=dev # or -export TETRA_IMAGE_TAG=staging +export FLASH_IMAGE_TAG=staging ``` ### Individual Overrides @@ -234,18 +234,18 @@ export TETRA_IMAGE_TAG=staging Override specific images: ```bash -export TETRA_GPU_IMAGE=my-registry/tetra-rp:custom -export TETRA_CPU_IMAGE=my-registry/tetra-rp-cpu:custom -export TETRA_LB_IMAGE=my-registry/tetra-rp-lb:custom -export TETRA_CPU_LB_IMAGE=my-registry/tetra-rp-lb-cpu:custom +export FLASH_GPU_IMAGE=my-registry/runpod-flash:custom +export FLASH_CPU_IMAGE=my-registry/runpod-flash-cpu:custom +export FLASH_LB_IMAGE=my-registry/runpod-flash-lb:custom +export FLASH_CPU_LB_IMAGE=my-registry/runpod-flash-lb-cpu:custom ``` ## Files Modified -- `src/tetra_rp/cli/commands/build_utils/manifest.py` - Uses constants -- `src/tetra_rp/cli/commands/test_mothership.py` - Uses constants -- `src/tetra_rp/core/resources/constants.py` - Centralizes constants -- `src/tetra_rp/core/resources/live_serverless.py` - Imports from constants +- `src/runpod_flash/cli/commands/build_utils/manifest.py` - Uses constants +- `src/runpod_flash/cli/commands/test_mothership.py` - Uses constants +- `src/runpod_flash/core/resources/constants.py` - Centralizes constants +- `src/runpod_flash/core/resources/live_serverless.py` - Imports from constants - `tests/unit/cli/commands/build_utils/test_manifest_mothership.py` - Updated tests ## Related Documentation @@ -259,7 +259,7 @@ export TETRA_CPU_LB_IMAGE=my-registry/tetra-rp-lb-cpu:custom To re-run this verification after future changes: ```bash -cd /Users/deanquinanola/Github/python/tetra-rp +cd /Users/deanquinanola/Github/python/runpod-flash uv run python3 scripts/test-image-constants.py ``` @@ -269,29 +269,29 @@ This script can be retained indefinitely and re-run to ensure the fix remains in ### Test Fails with "Module not found" -Make sure you're running from the tetra-rp directory: +Make sure you're running from the runpod-flash directory: ```bash -cd /Users/deanquinanola/Github/python/tetra-rp +cd /Users/deanquinanola/Github/python/runpod-flash ``` ### Constants Have Unexpected Values Check if environment variables are set: ```bash -echo $TETRA_IMAGE_TAG -echo $TETRA_CPU_LB_IMAGE +echo $FLASH_IMAGE_TAG +echo $FLASH_CPU_LB_IMAGE ``` Unset them if they're interfering: ```bash -unset TETRA_IMAGE_TAG TETRA_CPU_LB_IMAGE TETRA_LB_IMAGE +unset FLASH_IMAGE_TAG FLASH_CPU_LB_IMAGE FLASH_LB_IMAGE ``` ### Manifest Not Using Constants Verify imports in manifest.py: ```bash -grep "from tetra_rp.core.resources.constants import" src/tetra_rp/cli/commands/build_utils/manifest.py +grep "from runpod_flash.core.resources.constants import" src/runpod_flash/cli/commands/build_utils/manifest.py ``` ## Summary diff --git a/docs/Cross_Endpoint_Routing.md b/docs/Cross_Endpoint_Routing.md index 64d3f4be..222280b7 100644 --- a/docs/Cross_Endpoint_Routing.md +++ b/docs/Cross_Endpoint_Routing.md @@ -77,7 +77,7 @@ export RUNPOD_ENDPOINT_ID=gpu-endpoint-123 Define functions normally. The routing system decides execution location: ```python -from tetra_rp import stub +from runpod_flash import stub @stub.function() async def process_image(image_path: str) -> dict: @@ -385,7 +385,7 @@ graph TD #### 1. ProductionWrapper -**Location**: `src/tetra_rp/runtime/production_wrapper.py` +**Location**: `src/runpod_flash/runtime/production_wrapper.py` Intercepts function calls at the stub layer and routes to local or remote execution: @@ -440,7 +440,7 @@ args = cloudpickle.loads(base64.b64decode(serialized)) #### 2. ServiceRegistry -**Location**: `src/tetra_rp/runtime/service_registry.py` +**Location**: `src/runpod_flash/runtime/service_registry.py` Manages service discovery and manifest loading: @@ -529,7 +529,7 @@ class ServiceRegistry: #### 3. StateManagerClient -**Location**: `src/tetra_rp/runtime/state_manager_client.py` +**Location**: `src/runpod_flash/runtime/state_manager_client.py` GraphQL client for State Manager manifest persistence (used by mothership auto-provisioning): @@ -574,7 +574,7 @@ class StateManagerClient: #### 5. Exception Hierarchy -**Location**: `src/tetra_rp/runtime/exceptions.py` +**Location**: `src/runpod_flash/runtime/exceptions.py` Custom exceptions for cross-endpoint routing: @@ -618,7 +618,7 @@ except ManifestServiceUnavailableError as e: #### Stub Layer Integration -ProductionWrapper integrates with the stub execution layer in `src/tetra_rp/stubs/registry.py`: +ProductionWrapper integrates with the stub execution layer in `src/runpod_flash/stubs/registry.py`: ```python # Before: Direct stub execution @@ -654,7 +654,7 @@ endpoint_url = endpoint.url # e.g., "https://api.runpod.ai/v2/abc123" #### Runtime Config -**Location**: `src/tetra_rp/runtime/config.py` +**Location**: `src/runpod_flash/runtime/config.py` Centralized configuration constants: diff --git a/docs/Flash_Deploy_Guide.md b/docs/Flash_Deploy_Guide.md index e49fd8bd..6a98656f 100644 --- a/docs/Flash_Deploy_Guide.md +++ b/docs/Flash_Deploy_Guide.md @@ -96,7 +96,7 @@ flash deploy new production # Next: flash deploy send production ``` -**Implementation:** `src/tetra_rp/cli/commands/deploy.py:38-50` +**Implementation:** `src/runpod_flash/cli/commands/deploy.py:38-50` --- @@ -129,7 +129,7 @@ flash deploy send production # Deployment Complete ``` -**Implementation:** `src/tetra_rp/cli/commands/deploy.py:197-224` +**Implementation:** `src/runpod_flash/cli/commands/deploy.py:197-224` --- @@ -150,7 +150,7 @@ flash deploy list [--app-name ] - Active build ID - Creation timestamp -**Implementation:** `src/tetra_rp/cli/commands/deploy.py:27-135` +**Implementation:** `src/runpod_flash/cli/commands/deploy.py:27-135` --- @@ -174,7 +174,7 @@ flash deploy info [--app-name ] - Associated endpoints - Associated network volumes -**Implementation:** `src/tetra_rp/cli/commands/deploy.py:69-111` +**Implementation:** `src/runpod_flash/cli/commands/deploy.py:69-111` --- @@ -196,7 +196,7 @@ flash deploy delete [--app-name ] - Requires confirmation (twice for safety) - Cannot be undone -**Implementation:** `src/tetra_rp/cli/commands/deploy.py:237-270` +**Implementation:** `src/runpod_flash/cli/commands/deploy.py:237-270` --- @@ -221,12 +221,12 @@ sequenceDiagram Archive->>Developer: Build complete ``` -**Scanner** (`src/tetra_rp/cli/commands/build_utils/scanner.py`): +**Scanner** (`src/runpod_flash/cli/commands/build_utils/scanner.py`): - Decorators scanned: `@remote`, `@load_balanced`, `@cluster` - Extracts: function name, module path, async status, HTTP routing info - Groups functions by resource config -**Manifest Building** (`src/tetra_rp/cli/commands/build_utils/manifest.py`): +**Manifest Building** (`src/runpod_flash/cli/commands/build_utils/manifest.py`): - Structure: ```json { @@ -254,8 +254,8 @@ artifact.tar.gz ``` **Key Files:** -- `src/tetra_rp/cli/commands/build.py` - Entry point for `flash build` -- `src/tetra_rp/cli/commands/build_utils/manifest.py` - ManifestBuilder +- `src/runpod_flash/cli/commands/build.py` - Entry point for `flash build` +- `src/runpod_flash/cli/commands/build_utils/manifest.py` - ManifestBuilder --- @@ -271,13 +271,13 @@ sequenceDiagram ChildEndpoints->>StateManager: Query for peer endpoints
peer-to-peer discovery ``` -**Upload Process** (`src/tetra_rp/cli/commands/deploy.py:197-224`): +**Upload Process** (`src/runpod_flash/cli/commands/deploy.py:197-224`): 1. Archive uploaded to RunPod's built-in S3 storage 2. URL generated with temporary access 3. URL passed to mothership endpoint creation **Key Files:** -- `src/tetra_rp/cli/commands/deploy.py` - Deploy CLI commands +- `src/runpod_flash/cli/commands/deploy.py` - Deploy CLI commands --- @@ -303,20 +303,20 @@ sequenceDiagram **Key Components:** -**MothershipsProvisioner** (`src/tetra_rp/runtime/mothership_provisioner.py`): +**MothershipsProvisioner** (`src/runpod_flash/runtime/mothership_provisioner.py`): - `is_mothership()`: Check if endpoint is mothership (FLASH_IS_MOTHERSHIP=true) - `reconcile_children()`: Compute diff between desired and current state - Verifies child endpoints are deployed and healthy - Updates State Manager with reconciliation results -**ResourceManager** (`src/tetra_rp/core/resources/resource_manager.py`): +**ResourceManager** (`src/runpod_flash/core/resources/resource_manager.py`): - Singleton pattern (global resource registry) - Stores state in `.runpod/resources.pkl` with file locking - Tracks config hashes for drift detection (hash comparison) - Provisioned upfront by CLI before environment activation - Auto-migrates legacy resources -**StateManagerClient** (`src/tetra_rp/runtime/state_manager_client.py`): +**StateManagerClient** (`src/runpod_flash/runtime/state_manager_client.py`): - GraphQL client for persisting manifest state - Read-modify-write pattern for updates (3 GQL roundtrips) - Thread-safe with asyncio.Lock for concurrent updates @@ -331,9 +331,9 @@ sequenceDiagram 6. **Persist new state**: Update State Manager with current reconciliation results **Key Files:** -- `src/tetra_rp/runtime/mothership_provisioner.py` - Reconciliation logic -- `src/tetra_rp/core/resources/resource_manager.py` - Resource provisioning -- `src/tetra_rp/runtime/state_manager_client.py` - State persistence +- `src/runpod_flash/runtime/mothership_provisioner.py` - Reconciliation logic +- `src/runpod_flash/core/resources/resource_manager.py` - Resource provisioning +- `src/runpod_flash/runtime/state_manager_client.py` - State persistence --- @@ -363,14 +363,14 @@ sequenceDiagram Child->>Ready: Ready to execute functions ``` -**ManifestFetcher** (`src/tetra_rp/runtime/manifest_fetcher.py`): +**ManifestFetcher** (`src/runpod_flash/runtime/manifest_fetcher.py`): - Caches manifest with TTL (default: 300s) - Fetches from State Manager GraphQL API (source of truth) - Falls back to local flash_manifest.json if API unavailable - Updates local file with fetched data - Thread-safe with asyncio.Lock -**ServiceRegistry** (`src/tetra_rp/runtime/service_registry.py`): +**ServiceRegistry** (`src/runpod_flash/runtime/service_registry.py`): - Loads manifest to build function registry - Queries State Manager for peer endpoint URLs via GraphQL (peer-to-peer) - Returns mapping: `{resource_config_name: endpoint_url}` @@ -385,9 +385,9 @@ sequenceDiagram - `RUNPOD_ENDPOINT_ID`: This endpoint's RunPod endpoint ID **Key Files:** -- `src/tetra_rp/runtime/manifest_fetcher.py` - Manifest loading with caching -- `src/tetra_rp/runtime/service_registry.py` - Service discovery -- `src/tetra_rp/runtime/generic_handler.py` - Handler utilities +- `src/runpod_flash/runtime/manifest_fetcher.py` - Manifest loading with caching +- `src/runpod_flash/runtime/service_registry.py` - Service discovery +- `src/runpod_flash/runtime/generic_handler.py` - Handler utilities --- @@ -417,14 +417,14 @@ sequenceDiagram Stub->>Client: Return unwrapped result ``` -**Serialization** (`src/tetra_rp/runtime/serialization.py`): +**Serialization** (`src/runpod_flash/runtime/serialization.py`): - **Args/Kwargs**: cloudpickle → base64 - **Result**: cloudpickle → base64 - Max payload size: 10MB **Handler Routing**: -**Queue-Based** (`src/tetra_rp/runtime/generic_handler.py`): +**Queue-Based** (`src/runpod_flash/runtime/generic_handler.py`): Uses a factory function `create_handler(function_registry)` that returns a RunPod-compatible handler: @@ -453,16 +453,16 @@ def handler(job: Dict[str, Any]) -> Dict[str, Any]: } ``` -**Load-Balanced** (`src/tetra_rp/runtime/lb_handler.py`): +**Load-Balanced** (`src/runpod_flash/runtime/lb_handler.py`): - FastAPI app with user-defined HTTP routes - `/execute` endpoint for @remote execution (LiveLoadBalancer only) - User routes: HTTP methods + paths from manifest **Key Files:** -- `src/tetra_rp/runtime/generic_handler.py` - Queue-based handler -- `src/tetra_rp/runtime/lb_handler.py` - Load-balanced handler factory -- `src/tetra_rp/runtime/serialization.py` - cloudpickle serialization -- `src/tetra_rp/runtime/service_registry.py` - Cross-endpoint routing +- `src/runpod_flash/runtime/generic_handler.py` - Queue-based handler +- `src/runpod_flash/runtime/lb_handler.py` - Load-balanced handler factory +- `src/runpod_flash/runtime/serialization.py` - cloudpickle serialization +- `src/runpod_flash/runtime/service_registry.py` - Cross-endpoint routing --- @@ -474,7 +474,7 @@ The manifest is the contract between build-time and runtime. It defines all depl **Location**: Generated during `flash build` -**Builder**: `ManifestBuilder` in `src/tetra_rp/cli/commands/build_utils/manifest.py` +**Builder**: `ManifestBuilder` in `src/runpod_flash/cli/commands/build_utils/manifest.py` **Input**: - List of discovered `@remote` functions (from scanner) @@ -521,7 +521,7 @@ The manifest is the contract between build-time and runtime. It defines all depl - Load-balanced endpoints have method and path - No reserved paths (/execute, /ping) -**Code Reference**: `src/tetra_rp/cli/commands/build_utils/manifest.py:50-164` +**Code Reference**: `src/runpod_flash/cli/commands/build_utils/manifest.py:50-164` --- @@ -542,7 +542,7 @@ The manifest is the contract between build-time and runtime. It defines all depl 4. **Return to caller**: Cached manifest -**Code Reference**: `src/tetra_rp/runtime/manifest_fetcher.py:47-118` +**Code Reference**: `src/runpod_flash/runtime/manifest_fetcher.py:47-118` **Child Endpoint Side** - `ServiceRegistry`: @@ -560,7 +560,7 @@ The manifest is the contract between build-time and runtime. It defines all depl 4. **Cache endpoints**: Store for routing decisions -**Code Reference**: `src/tetra_rp/runtime/service_registry.py:29-80` +**Code Reference**: `src/runpod_flash/runtime/service_registry.py:29-80` --- @@ -599,7 +599,7 @@ Write: Mutation updateFlashBuildManifest **Performance**: Each update = 3 GQL roundtrips - Consider batching when provisioning many resources -**Code Reference**: `src/tetra_rp/runtime/state_manager_client.py:53-248` +**Code Reference**: `src/runpod_flash/runtime/state_manager_client.py:53-248` --- @@ -637,7 +637,7 @@ Resources are dynamically provisioned by the mothership during boot, based on th - New format: `{ResourceType:name: resource}` - Enables name-based lookup and drift detection -**Code Reference**: `src/tetra_rp/core/resources/resource_manager.py:22-150` +**Code Reference**: `src/runpod_flash/core/resources/resource_manager.py:22-150` --- @@ -683,7 +683,7 @@ await StateManagerClient.update_resource_state(mothership_id, resources) - If hashes differ: Resource has been modified, trigger update - Prevents unnecessary updates when resource unchanged -**Code Reference**: `src/tetra_rp/runtime/mothership_provisioner.py:1-150` +**Code Reference**: `src/runpod_flash/runtime/mothership_provisioner.py:1-150` --- @@ -744,7 +744,7 @@ serialized = base64.b64encode(cloudpickle.dumps(args)) deserialized = cloudpickle.loads(base64.b64decode(serialized)) ``` -**Code Reference**: `src/tetra_rp/runtime/serialization.py` +**Code Reference**: `src/runpod_flash/runtime/serialization.py` --- @@ -755,7 +755,7 @@ deserialized = cloudpickle.loads(base64.b64decode(serialized)) Uses a factory function `create_handler(function_registry)` that creates a RunPod-compatible handler: ```python -# src/tetra_rp/runtime/generic_handler.py - conceptual flow +# src/runpod_flash/runtime/generic_handler.py - conceptual flow def handler(job: Dict[str, Any]) -> Dict[str, Any]: # Extract job input job_input = job.get("input", {}) @@ -783,7 +783,7 @@ def handler(job: Dict[str, Any]) -> Dict[str, Any]: Uses `create_lb_handler(route_registry, include_execute=False)` factory: ```python -# src/tetra_rp/runtime/lb_handler.py - conceptual structure +# src/runpod_flash/runtime/lb_handler.py - conceptual structure app = FastAPI() # User-defined routes registered from route_registry @@ -818,8 +818,8 @@ async def execute_remote_function(request: Request) -> Dict[str, Any]: ``` **Code References**: -- `src/tetra_rp/runtime/generic_handler.py` - Queue-based handler -- `src/tetra_rp/runtime/lb_handler.py` - Load-balanced handler +- `src/runpod_flash/runtime/generic_handler.py` - Queue-based handler +- `src/runpod_flash/runtime/lb_handler.py` - Load-balanced handler --- @@ -828,7 +828,7 @@ async def execute_remote_function(request: Request) -> Dict[str, Any]: **ServiceRegistry** determines function endpoint: ```python -# src/tetra_rp/runtime/service_registry.py +# src/runpod_flash/runtime/service_registry.py registry = ServiceRegistry() # Lookup function's resource config @@ -1041,7 +1041,7 @@ Flash Deploy uses a dual-layer state system for reliability and consistency. - Exclusive lock for writes (single writer) - Prevents data corruption during concurrent access -**Code Reference**: `src/tetra_rp/core/resources/resource_manager.py:46-150` +**Code Reference**: `src/runpod_flash/core/resources/resource_manager.py:46-150` ### Remote State: RunPod State Manager (GraphQL API) @@ -1096,7 +1096,7 @@ On mothership boot: 4. Verify resource health and status 5. Persist reconciliation state to State Manager -**Code Reference**: `src/tetra_rp/runtime/state_manager_client.py` +**Code Reference**: `src/runpod_flash/runtime/state_manager_client.py` --- @@ -1127,7 +1127,7 @@ flash build --preview - Test endpoint auto-discovery - Verify container networking -**Code Reference**: `src/tetra_rp/cli/commands/preview.py` +**Code Reference**: `src/runpod_flash/cli/commands/preview.py` ### Local Docker Testing @@ -1142,10 +1142,10 @@ docker run -it \ -e FLASH_IS_MOTHERSHIP=true \ -e RUNPOD_API_KEY=$RUNPOD_API_KEY \ -v $(pwd)/.flash:/workspace/.flash \ - tetra-rp:latest + runpod-flash:latest # Run provisioner -python -m tetra_rp.runtime.mothership_provisioner +python -m runpod_flash.runtime.mothership_provisioner ``` ### Debugging Tips @@ -1163,7 +1163,7 @@ python -c "import json; print(json.dumps(json.load(open('flash_manifest.json')), **Check Local Resources**: ```python -from tetra_rp.core.resources.resource_manager import ResourceManager +from runpod_flash.core.resources.resource_manager import ResourceManager rm = ResourceManager() print(rm._resources) print(rm._resource_configs) @@ -1173,7 +1173,7 @@ print(rm._resource_configs) ```python # Add logging to ServiceRegistry import logging -logging.getLogger("tetra_rp.runtime.service_registry").setLevel(logging.DEBUG) +logging.getLogger("runpod_flash.runtime.service_registry").setLevel(logging.DEBUG) ``` --- @@ -1184,49 +1184,49 @@ logging.getLogger("tetra_rp.runtime.service_registry").setLevel(logging.DEBUG) | File | Purpose | |------|---------| -| `src/tetra_rp/cli/commands/deploy.py` | Deploy environment management commands | -| `src/tetra_rp/cli/commands/build.py` | Build packaging and archive creation | -| `src/tetra_rp/cli/commands/test_mothership.py` | Local mothership testing | +| `src/runpod_flash/cli/commands/deploy.py` | Deploy environment management commands | +| `src/runpod_flash/cli/commands/build.py` | Build packaging and archive creation | +| `src/runpod_flash/cli/commands/test_mothership.py` | Local mothership testing | ### Build System | File | Purpose | |------|---------| -| `src/tetra_rp/cli/commands/build_utils/scanner.py` | Scans for @remote decorators | -| `src/tetra_rp/cli/commands/build_utils/manifest.py` | Manifest builder and validation | +| `src/runpod_flash/cli/commands/build_utils/scanner.py` | Scans for @remote decorators | +| `src/runpod_flash/cli/commands/build_utils/manifest.py` | Manifest builder and validation | ### Resource Management | File | Purpose | |------|---------| -| `src/tetra_rp/core/resources/resource_manager.py` | Resource provisioning and state tracking | -| `src/tetra_rp/core/resources/base.py` | Base resource types | -| `src/tetra_rp/core/resources/serverless.py` | Serverless resource implementations | +| `src/runpod_flash/core/resources/resource_manager.py` | Resource provisioning and state tracking | +| `src/runpod_flash/core/resources/base.py` | Base resource types | +| `src/runpod_flash/core/resources/serverless.py` | Serverless resource implementations | ### Runtime: Manifest & State | File | Purpose | |------|---------| -| `src/tetra_rp/runtime/manifest_fetcher.py` | Manifest loading from local .flash/ directory | -| `src/tetra_rp/runtime/state_manager_client.py` | GraphQL client for peer-to-peer service discovery | -| `src/tetra_rp/runtime/mothership_provisioner.py` | Auto-provisioning logic | +| `src/runpod_flash/runtime/manifest_fetcher.py` | Manifest loading from local .flash/ directory | +| `src/runpod_flash/runtime/state_manager_client.py` | GraphQL client for peer-to-peer service discovery | +| `src/runpod_flash/runtime/mothership_provisioner.py` | Auto-provisioning logic | ### Runtime: Execution | File | Purpose | |------|---------| -| `src/tetra_rp/runtime/generic_handler.py` | Queue-based handler factory | -| `src/tetra_rp/runtime/lb_handler.py` | Load-balanced (FastAPI) handler factory | -| `src/tetra_rp/runtime/service_registry.py` | Service discovery and routing | -| `src/tetra_rp/runtime/serialization.py` | cloudpickle serialization/deserialization | +| `src/runpod_flash/runtime/generic_handler.py` | Queue-based handler factory | +| `src/runpod_flash/runtime/lb_handler.py` | Load-balanced (FastAPI) handler factory | +| `src/runpod_flash/runtime/service_registry.py` | Service discovery and routing | +| `src/runpod_flash/runtime/serialization.py` | cloudpickle serialization/deserialization | ### Utilities | File | Purpose | |------|---------| -| `src/tetra_rp/core/utils/file_lock.py` | Cross-platform file locking | -| `src/tetra_rp/core/utils/singleton.py` | Singleton pattern implementation | -| `src/tetra_rp/runtime/config.py` | Runtime configuration constants | +| `src/runpod_flash/core/utils/file_lock.py` | Cross-platform file locking | +| `src/runpod_flash/core/utils/singleton.py` | Singleton pattern implementation | +| `src/runpod_flash/runtime/config.py` | Runtime configuration constants | --- diff --git a/docs/Flash_SDK_Reference.md b/docs/Flash_SDK_Reference.md index 55ee5616..1dd65e83 100644 --- a/docs/Flash_SDK_Reference.md +++ b/docs/Flash_SDK_Reference.md @@ -4,13 +4,13 @@ This section documents the complete Flash SDK API. Reference this section when b ### Overview -**tetra-rp** is the underlying SDK powering the Flash framework. It provides: +**runpod-flash** is the underlying SDK powering the Flash framework. It provides: - The `@remote` decorator for marking functions as distributed workers - Resource configuration classes for defining compute requirements - GPU/CPU specifications and pricing models - Queue-based (reliable, retry-enabled) and load-balanced (low-latency HTTP) execution models -**Import from `tetra_rp`, not `flash`.** The Flash CLI wraps tetra-rp functionality. +**Import from `runpod_flash`, not `flash`.** The Flash CLI wraps runpod-flash functionality. ### Main Exports @@ -18,26 +18,26 @@ Core imports for Flash Examples: ```python # Main decorator -from tetra_rp import remote +from runpod_flash import remote # Resource configuration classes (queue-based) -from tetra_rp import LiveServerless, CpuLiveServerless # Development -from tetra_rp import ServerlessEndpoint, CpuServerlessEndpoint # Production +from runpod_flash import LiveServerless, CpuLiveServerless # Development +from runpod_flash import ServerlessEndpoint, CpuServerlessEndpoint # Production # Resource configuration classes (load-balanced, HTTP) -from tetra_rp import LiveLoadBalancer, CpuLiveLoadBalancer # Development -from tetra_rp import LoadBalancerSlsResource, CpuLoadBalancerSlsResource # Production +from runpod_flash import LiveLoadBalancer, CpuLiveLoadBalancer # Development +from runpod_flash import LoadBalancerSlsResource, CpuLoadBalancerSlsResource # Production # GPU and CPU specifications -from tetra_rp import GpuGroup, CpuInstanceType +from runpod_flash import GpuGroup, CpuInstanceType # Advanced features -from tetra_rp import NetworkVolume, PodTemplate, CudaVersion, DataCenter +from runpod_flash import NetworkVolume, PodTemplate, CudaVersion, DataCenter ``` ### The @remote Decorator -The `@remote` decorator marks a function for distributed execution. It's the core of tetra-rp. +The `@remote` decorator marks a function for distributed execution. It's the core of runpod-flash. #### Complete Signature @@ -144,7 +144,7 @@ class ResourceConfig: **LiveServerless - Development GPU** ```python -from tetra_rp import remote, LiveServerless, GpuGroup +from runpod_flash import remote, LiveServerless, GpuGroup gpu_config = LiveServerless( name="02_ml_inference_gpu_worker", @@ -174,7 +174,7 @@ else: **CpuLiveServerless - Development CPU** ```python -from tetra_rp import remote, CpuLiveServerless +from runpod_flash import remote, CpuLiveServerless cpu_config = CpuLiveServerless( name="01_getting_started_cpu_worker", @@ -197,7 +197,7 @@ async def process_data(items: list) -> dict: **ServerlessEndpoint - Production GPU** ```python -from tetra_rp import remote, ServerlessEndpoint, GpuGroup +from runpod_flash import remote, ServerlessEndpoint, GpuGroup prod_config = ServerlessEndpoint( name="02_ml_inference_gpu_prod", @@ -216,7 +216,7 @@ async def production_inference(data: dict) -> dict: **LiveLoadBalancer - Development GPU** ```python -from tetra_rp import remote, LiveLoadBalancer, GpuGroup +from runpod_flash import remote, LiveLoadBalancer, GpuGroup lb_config = LiveLoadBalancer( name="03_load_balanced_gpu", @@ -234,7 +234,7 @@ async def real_time_inference(data: dict) -> dict: **LoadBalancerSlsResource - Production GPU** ```python -from tetra_rp import remote, LoadBalancerSlsResource, GpuGroup +from runpod_flash import remote, LoadBalancerSlsResource, GpuGroup prod_lb_config = LoadBalancerSlsResource( name="03_load_balanced_prod", @@ -253,7 +253,7 @@ async def production_realtime(data: dict) -> dict: Complete `GpuGroup` enum with VRAM specifications: ```python -from tetra_rp import GpuGroup +from runpod_flash import GpuGroup # Ampere GPUs (Previous generation, lower cost) GpuGroup.AMPERE_16 # RTX A4000, 16GB VRAM @@ -277,7 +277,7 @@ GpuGroup.ANY # Any available GPU (not recommended for production) **Multiple GPU Selection:** ```python -from tetra_rp import GpuGroup +from runpod_flash import GpuGroup config = LiveServerless( name="multi_gpu_example", @@ -290,7 +290,7 @@ config = LiveServerless( CPU configurations using `CpuInstanceType` enum: ```python -from tetra_rp import CpuInstanceType +from runpod_flash import CpuInstanceType # CPU instances (vCPU count) CpuInstanceType.CPU_2 # 2 vCPU, ~2GB RAM @@ -303,7 +303,7 @@ CpuInstanceType.CPU_32 # 32 vCPU, ~32GB RAM **Usage:** ```python -from tetra_rp import CpuLiveServerless, CpuInstanceType +from runpod_flash import CpuLiveServerless, CpuInstanceType config = CpuLiveServerless( name="cpu_processing", @@ -396,7 +396,7 @@ def my_function(data: dict) -> dict: # No async! #### Arguments Must Be Serializable -tetra-rp uses **cloudpickle** to serialize function arguments. Standard types work: +runpod-flash uses **cloudpickle** to serialize function arguments. Standard types work: ```python # ✅ Serializable types @@ -552,7 +552,7 @@ class JobOutput: **Usage:** ```python -from tetra_rp import remote, LiveServerless +from runpod_flash import remote, LiveServerless config = LiveServerless(name="my_job", gpus=[GpuGroup.ANY]) @@ -579,7 +579,7 @@ else: Load-balanced resources return your value directly as HTTP response: ```python -from tetra_rp import remote, LiveLoadBalancer, GpuGroup +from runpod_flash import remote, LiveLoadBalancer, GpuGroup lb_config = LiveLoadBalancer(name="api", gpus=[GpuGroup.ANY]) @@ -675,7 +675,7 @@ async def process_image(image_url: str) -> dict: #### Cloudpickle Caching -Arguments are hashed and cached by tetra-rp. Identical arguments in rapid succession reuse cached versions. +Arguments are hashed and cached by runpod-flash. Identical arguments in rapid succession reuse cached versions. ```python # First call - argument cached @@ -714,7 +714,7 @@ result = await my_function({"input": "value"}) Mount persistent storage for large models or datasets: ```python -from tetra_rp import remote, LiveServerless, NetworkVolume +from runpod_flash import remote, LiveServerless, NetworkVolume volume = NetworkVolume( name="model_storage", @@ -744,7 +744,7 @@ async def inference(prompt: str) -> dict: Advanced: Custom pod configuration for specialized requirements: ```python -from tetra_rp import PodTemplate +from runpod_flash import PodTemplate pod_template = PodTemplate( # Custom resource limits @@ -786,7 +786,7 @@ async def configurable_function(data: dict) -> dict: Execute methods on remote classes: ```python -from tetra_rp import remote, LiveServerless +from runpod_flash import remote, LiveServerless @remote(resource_config=config) async def run_model(data: dict) -> dict: @@ -846,13 +846,13 @@ async def inference(request: InferenceRequest) -> dict: ```python import asyncio -from tetra_rp import remote, LiveServerless, LiveLoadBalancer +from runpod_flash import remote, LiveServerless, LiveLoadBalancer config = LiveServerless(name="retry_example", gpus=[GpuGroup.ANY]) @remote(resource_config=config, max_retries=3) async def unreliable_operation(data: dict) -> dict: - # tetra-rp handles retries automatically for queue-based + # runpod-flash handles retries automatically for queue-based async def flaky_external_api(data): # Your API call logic here import httpx diff --git a/docs/GPU_Provisioning.md b/docs/GPU_Provisioning.md index f429aabb..a6759277 100644 --- a/docs/GPU_Provisioning.md +++ b/docs/GPU_Provisioning.md @@ -252,7 +252,7 @@ class LiveServerless(LiveServerlessMixin, ServerlessEndpoint): @property def _live_image(self) -> str: - return TETRA_GPU_IMAGE # Locked to GPU-optimized image + return FLASH_GPU_IMAGE # Locked to GPU-optimized image @property def imageName(self): @@ -388,7 +388,7 @@ live_endpoint = LiveServerless( workersMax=8, workersMin=1 ) -# imageName automatically locked to TETRA_GPU_IMAGE +# imageName automatically locked to FLASH_GPU_IMAGE ``` ### High-Memory Workloads with Conservative Scaling diff --git a/docs/LoadBalancer_Runtime_Architecture.md b/docs/LoadBalancer_Runtime_Architecture.md index 782b815f..297ddb5e 100644 --- a/docs/LoadBalancer_Runtime_Architecture.md +++ b/docs/LoadBalancer_Runtime_Architecture.md @@ -15,7 +15,7 @@ graph TD A["User Code"] -->|flash build| B["Package Application"] B -->|FastAPI App| C["Flash Manifest"] C -->|flash deploy| D["Push to RunPod"] - D -->|Create Container| E["RunPod Container
tetra-rp-lb image"] + D -->|Create Container| E["RunPod Container
runpod-flash-lb image"] E --> F["FastAPI Server
uvicorn on port 8000"] F --> G["Load your application"] G --> H["Endpoint Ready"] @@ -45,7 +45,7 @@ The runtime loads your application using the manifest and route registry: # and executes your @remote decorated functions via FastAPI from fastapi import FastAPI -from tetra_rp.runtime.lb_handler import create_lb_handler +from runpod_flash.runtime.lb_handler import create_lb_handler # User functions are discovered and registered at runtime # Routes are configured based on @remote decorators with HTTP method and path @@ -58,7 +58,7 @@ if __name__ == "__main__": ``` **Container Setup:** -- Base image: `runpod/tetra-rp-lb:latest` (contains FastAPI, uvicorn, dependencies) +- Base image: `runpod/runpod-flash-lb:latest` (contains FastAPI, uvicorn, dependencies) - Entrypoint: Loads manifest and starts FastAPI server - Port: 8000 (internal) - RunPod exposes this via HTTPS endpoint URL @@ -170,7 +170,7 @@ sequenceDiagram ```python # Local code - after deployment api = LoadBalancerSlsResource(name="user-service", - imageName="runpod/tetra-rp-lb:latest") + imageName="runpod/runpod-flash-lb:latest") # Deploy the endpoint (generates endpoint_url automatically) await api.deploy() @@ -634,9 +634,9 @@ RunPod polls /ping every 30 seconds ### Image Selection ``` -tetra-rp-lb:latest (default) +runpod-flash-lb:latest (default) - FastAPI + uvicorn pre-installed -- Tetra runtime dependencies +- Flash runtime dependencies - Optimized for LB endpoints Custom image: @@ -650,7 +650,7 @@ Custom image: ```python LoadBalancerSlsResource( name="my-api", - imageName="runpod/tetra-rp-lb:latest", + imageName="runpod/runpod-flash-lb:latest", gpus=[GpuGroup.AMPERE_80], # Optional: if compute needed instanceIds=[...], # Or specify CPU instances workersMax=5, # Max concurrent workers diff --git a/docs/Load_Balancer_Endpoints.md b/docs/Load_Balancer_Endpoints.md index 0c7a3808..aaceb01e 100644 --- a/docs/Load_Balancer_Endpoints.md +++ b/docs/Load_Balancer_Endpoints.md @@ -148,7 +148,7 @@ This document focuses on the `LoadBalancerSlsResource` class implementation and ### Basic Provisioning ```python -from tetra_rp import LoadBalancerSlsResource +from runpod_flash import LoadBalancerSlsResource # Create a load-balanced endpoint mothership = LoadBalancerSlsResource( diff --git a/docs/Resource_Config_Drift_Detection.md b/docs/Resource_Config_Drift_Detection.md index fd180f58..99a0b4ba 100644 --- a/docs/Resource_Config_Drift_Detection.md +++ b/docs/Resource_Config_Drift_Detection.md @@ -23,7 +23,7 @@ graph LR Each resource computes a hash excluding runtime-assigned fields: ```python -# File: src/tetra_rp/core/resources/serverless.py +# File: src/runpod_flash/core/resources/serverless.py RUNTIME_FIELDS: ClassVar[Set[str]] = { "template", # Assigned by API @@ -66,7 +66,7 @@ def config_hash(self) -> str: When ResourceManager registers a resource, it stores the hash: ```python -# File: src/tetra_rp/core/resources/resource_manager.py +# File: src/runpod_flash/core/resources/resource_manager.py def _add_resource(self, uid: str, resource: DeployableResource): """Add a resource to the manager.""" @@ -108,7 +108,7 @@ async def get_or_deploy_resource(self, config: DeployableResource): CPU LoadBalancers have a customized hash that includes only CPU-relevant fields: ```python -# File: src/tetra_rp/core/resources/load_balancer_sls_resource.py +# File: src/runpod_flash/core/resources/load_balancer_sls_resource.py @property def config_hash(self) -> str: @@ -141,7 +141,7 @@ def config_hash(self) -> str: ### Basic Deployment with Auto Drift Detection ```python -from tetra_rp import CpuLoadBalancerSlsResource +from runpod_flash import CpuLoadBalancerSlsResource # Define resource lb = CpuLoadBalancerSlsResource( @@ -350,9 +350,9 @@ cpu_fields = { ## Related Files -- **Implementation:** `src/tetra_rp/core/resources/serverless.py` (config_hash) -- **CPU Variant:** `src/tetra_rp/core/resources/load_balancer_sls_resource.py` (config_hash override) -- **Resource Manager:** `src/tetra_rp/core/resources/resource_manager.py` (drift detection logic) +- **Implementation:** `src/runpod_flash/core/resources/serverless.py` (config_hash) +- **CPU Variant:** `src/runpod_flash/core/resources/load_balancer_sls_resource.py` (config_hash override) +- **Resource Manager:** `src/runpod_flash/core/resources/resource_manager.py` (drift detection logic) - **Tests:** `tests/unit/resources/test_load_balancer_drift.py` (42 tests) --- diff --git a/docs/Using_Remote_With_LoadBalancer.md b/docs/Using_Remote_With_LoadBalancer.md index 0b974e17..b68a3504 100644 --- a/docs/Using_Remote_With_LoadBalancer.md +++ b/docs/Using_Remote_With_LoadBalancer.md @@ -41,7 +41,7 @@ Use **Queue-Based** when you need: For local development, use `LiveLoadBalancer`: ```python -from tetra_rp import LiveLoadBalancer, remote +from runpod_flash import LiveLoadBalancer, remote # Create load-balanced endpoint api = LiveLoadBalancer(name="example-api") @@ -86,7 +86,7 @@ Load-balanced endpoints require explicit HTTP routing metadata in the `@remote` Multiple functions can share a single LoadBalancerSlsResource with different routes: ```python -from tetra_rp import LiveLoadBalancer, remote +from runpod_flash import LiveLoadBalancer, remote api = LiveLoadBalancer(name="user-service") @@ -134,7 +134,7 @@ Attempting to use `/ping` or `/execute` as user-defined routes will raise a vali For local development and testing, use `LiveLoadBalancer` instead of `LoadBalancerSlsResource`: ```python -from tetra_rp import LiveLoadBalancer, remote +from runpod_flash import LiveLoadBalancer, remote api = LiveLoadBalancer(name="my-api") @@ -149,7 +149,7 @@ async def test(): ``` **Key differences:** -- `LiveLoadBalancer` locks image to Tetra LB runtime (tetra-rp-lb) +- `LiveLoadBalancer` locks image to Flash LB runtime (runpod/flash-lb) - Functions execute directly without deployment - Ideal for development and CI/CD testing - Same `@remote` decorator interface as production @@ -158,7 +158,7 @@ async def test(): ```python import pytest -from tetra_rp import LiveLoadBalancer, remote +from runpod_flash import LiveLoadBalancer, remote api = LiveLoadBalancer(name="test-api") @@ -308,14 +308,14 @@ Here's a full example with multiple routes, error handling, and testing: user_service.py - Example load-balanced API service """ -from tetra_rp import LoadBalancerSlsResource, remote +from runpod_flash import LoadBalancerSlsResource, remote from typing import Optional # For production, use LoadBalancerSlsResource # For local development, use LiveLoadBalancer api = LoadBalancerSlsResource( name="user-service", - imageName="runpod/tetra-rp-lb:latest" + imageName="runpod/runpod-flash-lb:latest" ) class UserNotFound(Exception): @@ -384,7 +384,7 @@ test_user_service.py """ import pytest -from tetra_rp import LiveLoadBalancer, remote +from runpod_flash import LiveLoadBalancer, remote from typing import Optional # Use LiveLoadBalancer for testing @@ -508,7 +508,7 @@ See `docs/Load_Balancer_Endpoints.md` for detailed architecture and configuratio ### LiveLoadBalancer A test/development variant of LoadBalancerSlsResource: -- Locks to Tetra LB image +- Locks to Flash LB image - Enables direct function calls without deployment - Same decorator interface as production diff --git a/pyproject.toml b/pyproject.toml index 5081483f..60ebe407 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] -name = "tetra_rp" -version = "0.25.2" +name = "runpod-flash" +version = "0.25.1" description = "A Python library for distributed inference and serving of machine learning models" authors = [ { name = "Runpod", email = "engineer@runpod.io" }, @@ -43,7 +43,7 @@ test = [ ] [project.scripts] -flash = "tetra_rp.cli.main:app" +flash = "runpod_flash.cli.main:app" [build-system] requires = ["setuptools>=42", "wheel"] @@ -53,7 +53,7 @@ build-backend = "setuptools.build_meta" where = ["src"] [tool.setuptools.package-data] -tetra_rp = [ +runpod_flash = [ "cli/utils/skeleton_template/**/*", "cli/utils/skeleton_template/**/.*", ] @@ -66,7 +66,7 @@ python_functions = ["test_*"] addopts = [ "-v", "--tb=short", - "--cov=tetra_rp", + "--cov=runpod_flash", "--cov-report=term-missing", "--cov-fail-under=65" ] @@ -119,7 +119,7 @@ ignore_missing_imports = true [tool.coverage.run] parallel = true branch = false -source = ["tetra_rp"] +source = ["runpod_flash"] omit = [ "*/tests/*", "*/test_*.py", @@ -143,8 +143,8 @@ exclude_lines = [ [tool.coverage.paths] source = [ - "src/tetra_rp", - "*/site-packages/tetra_rp", + "src/runpod_flash", + "*/site-packages/runpod_flash", ] [tool.ruff] diff --git a/release-please-config.json b/release-please-config.json index 9a5b37e3..bfc0a557 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -2,7 +2,7 @@ "bootstrap-sha": "d64b7f22e1d320e775a7a4561b04e4bd26b0470f", "packages": { ".": { - "package-name": "tetra-rp", + "package-name": "runpod-flash", "release-type": "python", "include-component-in-tag": false, "changelog-sections": [ diff --git a/scripts/ast_to_sqlite.py b/scripts/ast_to_sqlite.py index e8f3205d..86c5a825 100644 --- a/scripts/ast_to_sqlite.py +++ b/scripts/ast_to_sqlite.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""AST-based code indexer for tetra-rp framework. +"""AST-based code indexer for runpod-flash framework. Extracts Python symbols (classes, functions, methods) and stores them in SQLite for fast symbol lookup and exploration. Reduces token usage by ~85% when exploring diff --git a/scripts/code_intel.py b/scripts/code_intel.py index fdd25fc8..8752b0c3 100644 --- a/scripts/code_intel.py +++ b/scripts/code_intel.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 -"""Code intelligence query interface for tetra-rp framework. +"""Code intelligence query interface for runpod-flash framework. Fast symbol lookup for exploring framework codebase. Reduces token usage by ~85% -when exploring tetra-rp framework structure with Claude Code. +when exploring runpod-flash framework structure with Claude Code. Usage: uv run python scripts/code_intel.py find @@ -21,7 +21,7 @@ app = typer.Typer( name="code-intel", - help="Query tetra-rp code intelligence index", + help="Query runpod-flash code intelligence index", no_args_is_help=True, ) console = Console() diff --git a/scripts/mcp_code_intel_server.py b/scripts/mcp_code_intel_server.py index d60a9564..e13483ef 100644 --- a/scripts/mcp_code_intel_server.py +++ b/scripts/mcp_code_intel_server.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""MCP server for tetra-rp code intelligence. +"""MCP server for runpod-flash code intelligence. Provides tools for querying the SQLite code intelligence database. Claude Code automatically discovers and uses these tools. @@ -30,7 +30,7 @@ MAX_DECORATOR_RESULTS = 100 # Initialize MCP server -server = Server("tetra-code-intel") +server = Server("flash-code-intel") def should_reindex() -> bool: @@ -231,7 +231,7 @@ async def list_tools() -> list[types.Tool]: return [ types.Tool( name="find_symbol", - description="Search for a symbol (class, function, method) by name in the tetra-rp framework. " + description="Search for a symbol (class, function, method) by name in the runpod-flash framework. " "Returns symbol name, kind, signature, file location, and docstring. " "Use this instead of reading full files when exploring code structure.", inputSchema={ @@ -247,7 +247,7 @@ async def list_tools() -> list[types.Tool]: ), types.Tool( name="list_classes", - description="List all classes in the tetra-rp framework with their signatures and locations. " + description="List all classes in the runpod-flash framework with their signatures and locations. " "Use this to understand the framework's class hierarchy without reading files.", inputSchema={"type": "object", "properties": {}}, ), @@ -404,7 +404,7 @@ async def handle_call_tool( results = cursor.fetchall() conn.close() - output = f"# Classes in tetra-rp Framework ({len(results)} found)\n\n" + output = f"# Classes in runpod-flash Framework ({len(results)} found)\n\n" current_file = None for row in results: file_path, name_val, sig, line, docstring = ( @@ -622,7 +622,7 @@ async def main() -> None: read_stream, write_stream, InitializationOptions( - server_name="tetra-code-intel", + server_name="flash-code-intel", server_version="1.0.0", capabilities=server.get_capabilities( notification_options=NotificationOptions(), diff --git a/scripts/test-image-constants.py b/scripts/test-image-constants.py index 98f03b76..e527c659 100755 --- a/scripts/test-image-constants.py +++ b/scripts/test-image-constants.py @@ -47,38 +47,38 @@ def test_constants_exist(): """Test that all constants are defined""" print(f"\n{BLUE}Test Suite 1: Constants Definition{END}") - from tetra_rp.core.resources.constants import ( - TETRA_IMAGE_TAG, - TETRA_GPU_IMAGE, - TETRA_CPU_IMAGE, - TETRA_LB_IMAGE, - TETRA_CPU_LB_IMAGE, + from runpod_flash.core.resources.constants import ( + FLASH_IMAGE_TAG, + FLASH_GPU_IMAGE, + FLASH_CPU_IMAGE, + FLASH_LB_IMAGE, + FLASH_CPU_LB_IMAGE, DEFAULT_WORKERS_MIN, DEFAULT_WORKERS_MAX, ) - test("TETRA_IMAGE_TAG defined", TETRA_IMAGE_TAG is not None) - test("TETRA_GPU_IMAGE defined", TETRA_GPU_IMAGE is not None) - test("TETRA_CPU_IMAGE defined", TETRA_CPU_IMAGE is not None) - test("TETRA_LB_IMAGE defined", TETRA_LB_IMAGE is not None) - test("TETRA_CPU_LB_IMAGE defined", TETRA_CPU_LB_IMAGE is not None) + test("FLASH_IMAGE_TAG defined", FLASH_IMAGE_TAG is not None) + test("FLASH_GPU_IMAGE defined", FLASH_GPU_IMAGE is not None) + test("FLASH_CPU_IMAGE defined", FLASH_CPU_IMAGE is not None) + test("FLASH_LB_IMAGE defined", FLASH_LB_IMAGE is not None) + test("FLASH_CPU_LB_IMAGE defined", FLASH_CPU_LB_IMAGE is not None) test("DEFAULT_WORKERS_MIN is 1", DEFAULT_WORKERS_MIN == 1) test("DEFAULT_WORKERS_MAX is 3", DEFAULT_WORKERS_MAX == 3) - print(f" Constants values (with TETRA_IMAGE_TAG={TETRA_IMAGE_TAG}):") - print(f" TETRA_GPU_IMAGE: {TETRA_GPU_IMAGE}") - print(f" TETRA_CPU_IMAGE: {TETRA_CPU_IMAGE}") - print(f" TETRA_LB_IMAGE: {TETRA_LB_IMAGE}") - print(f" TETRA_CPU_LB_IMAGE: {TETRA_CPU_LB_IMAGE}") + print(f" Constants values (with FLASH_IMAGE_TAG={FLASH_IMAGE_TAG}):") + print(f" FLASH_GPU_IMAGE: {FLASH_GPU_IMAGE}") + print(f" FLASH_CPU_IMAGE: {FLASH_CPU_IMAGE}") + print(f" FLASH_LB_IMAGE: {FLASH_LB_IMAGE}") + print(f" FLASH_CPU_LB_IMAGE: {FLASH_CPU_LB_IMAGE}") def test_manifest_builder(): """Test that manifest builder uses constants""" print(f"\n{BLUE}Test Suite 2: Manifest Builder Integration{END}") - from tetra_rp.cli.commands.build_utils.manifest import ManifestBuilder - from tetra_rp.core.resources.constants import ( - TETRA_CPU_LB_IMAGE, + from runpod_flash.cli.commands.build_utils.manifest import ManifestBuilder + from runpod_flash.core.resources.constants import ( + FLASH_CPU_LB_IMAGE, DEFAULT_WORKERS_MIN, DEFAULT_WORKERS_MAX, ) @@ -91,8 +91,8 @@ def test_manifest_builder(): ) test( - "Mothership uses TETRA_CPU_LB_IMAGE", - mothership["imageName"] == TETRA_CPU_LB_IMAGE, + "Mothership uses FLASH_CPU_LB_IMAGE", + mothership["imageName"] == FLASH_CPU_LB_IMAGE, f"Got {mothership['imageName']}", ) test( @@ -116,11 +116,11 @@ def test_live_serverless(): """Test that LiveServerless uses constants""" print(f"\n{BLUE}Test Suite 3: LiveServerless Integration{END}") - from tetra_rp import LiveServerless, CpuLiveLoadBalancer, LiveLoadBalancer - from tetra_rp.core.resources.constants import ( - TETRA_GPU_IMAGE, - TETRA_LB_IMAGE, - TETRA_CPU_LB_IMAGE, + from runpod_flash import LiveServerless, CpuLiveLoadBalancer, LiveLoadBalancer + from runpod_flash.core.resources.constants import ( + FLASH_GPU_IMAGE, + FLASH_LB_IMAGE, + FLASH_CPU_LB_IMAGE, ) gpu_ls = LiveServerless(name="test-gpu") @@ -128,18 +128,18 @@ def test_live_serverless(): cpu_lb = CpuLiveLoadBalancer(name="test-cpu-lb") test( - "LiveServerless uses TETRA_GPU_IMAGE", - gpu_ls.imageName == TETRA_GPU_IMAGE, + "LiveServerless uses FLASH_GPU_IMAGE", + gpu_ls.imageName == FLASH_GPU_IMAGE, f"Got {gpu_ls.imageName}", ) test( - "LiveLoadBalancer uses TETRA_LB_IMAGE", - gpu_lb.imageName == TETRA_LB_IMAGE, + "LiveLoadBalancer uses FLASH_LB_IMAGE", + gpu_lb.imageName == FLASH_LB_IMAGE, f"Got {gpu_lb.imageName}", ) test( - "CpuLiveLoadBalancer uses TETRA_CPU_LB_IMAGE", - cpu_lb.imageName == TETRA_CPU_LB_IMAGE, + "CpuLiveLoadBalancer uses FLASH_CPU_LB_IMAGE", + cpu_lb.imageName == FLASH_CPU_LB_IMAGE, f"Got {cpu_lb.imageName}", ) @@ -153,7 +153,7 @@ def test_env_var_override(): """Test environment variable override in subprocess""" print(f"\n{BLUE}Test Suite 4: Environment Variable Override{END}") - # Test with TETRA_IMAGE_TAG=dev in subprocess + # Test with FLASH_IMAGE_TAG=dev in subprocess result = subprocess.run( [ sys.executable, @@ -163,17 +163,17 @@ def test_env_var_override(): import os sys.path.insert(0, 'src') -from tetra_rp.core.resources.constants import ( - TETRA_IMAGE_TAG, - TETRA_CPU_LB_IMAGE, +from runpod_flash.core.resources.constants import ( + FLASH_IMAGE_TAG, + FLASH_CPU_LB_IMAGE, ) -assert TETRA_IMAGE_TAG == "dev", f"Expected dev, got {TETRA_IMAGE_TAG}" -assert TETRA_CPU_LB_IMAGE == "runpod/tetra-rp-lb-cpu:dev", f"Got {TETRA_CPU_LB_IMAGE}" -print(f"OK:{TETRA_CPU_LB_IMAGE}") +assert FLASH_IMAGE_TAG == "dev", f"Expected dev, got {FLASH_IMAGE_TAG}" +assert FLASH_CPU_LB_IMAGE == "runpod/flash-lb-cpu:dev", f"Got {FLASH_CPU_LB_IMAGE}" +print(f"OK:{FLASH_CPU_LB_IMAGE}") """, ], - env={**dict(os.environ), "TETRA_IMAGE_TAG": "dev"}, + env={**dict(os.environ), "FLASH_IMAGE_TAG": "dev"}, capture_output=True, text=True, cwd=Path(__file__).parent.parent, @@ -181,15 +181,15 @@ def test_env_var_override(): success = result.returncode == 0 and "OK:" in result.stdout test( - "TETRA_IMAGE_TAG=dev override works", + "FLASH_IMAGE_TAG=dev override works", success, result.stderr if not success else "", ) if success: image = result.stdout.split("OK:")[1].strip() - print(" With TETRA_IMAGE_TAG=dev:") - print(f" TETRA_CPU_LB_IMAGE: {image}") + print(" With FLASH_IMAGE_TAG=dev:") + print(f" FLASH_CPU_LB_IMAGE: {image}") def test_no_hardcoded_values(): @@ -198,13 +198,13 @@ def test_no_hardcoded_values(): manifest_file = ( Path(__file__).parent.parent - / "src/tetra_rp/cli/commands/build_utils/manifest.py" + / "src/runpod_flash/cli/commands/build_utils/manifest.py" ) content = manifest_file.read_text() hardcoded_patterns = [ - "runpod/tetra-rp-lb-cpu:latest", - "runpod/tetra-rp-lb:latest", + "runpod/flash-lb-cpu:latest", + "runpod/flash-lb:latest", 'imageName": "runpod/', ] @@ -216,12 +216,12 @@ def test_no_hardcoded_values(): # Check that manifest.py imports the constants test( - "Manifest imports TETRA_CPU_LB_IMAGE", - "TETRA_CPU_LB_IMAGE" in content, + "Manifest imports FLASH_CPU_LB_IMAGE", + "FLASH_CPU_LB_IMAGE" in content, ) test( - "Manifest imports TETRA_LB_IMAGE", - "TETRA_LB_IMAGE" in content, + "Manifest imports FLASH_LB_IMAGE", + "FLASH_LB_IMAGE" in content, ) test( "Manifest imports DEFAULT_WORKERS_MIN", diff --git a/scripts/validate-wheel.sh b/scripts/validate-wheel.sh index 15792b62..7e4dd517 100755 --- a/scripts/validate-wheel.sh +++ b/scripts/validate-wheel.sh @@ -11,24 +11,24 @@ cd "$PROJECT_DIR" echo "Building wheel..." uv build -WHEEL_FILE=$(ls -t dist/tetra_rp-*.whl | head -1) +WHEEL_FILE=$(ls -t dist/runpod_flash-*.whl | head -1) echo "Testing wheel: $WHEEL_FILE" echo "" # Check wheel contents without installing echo "Checking wheel contents..." REQUIRED_TEMPLATE_FILES=( - "tetra_rp/cli/utils/skeleton_template/.env.example" - "tetra_rp/cli/utils/skeleton_template/.gitignore" - "tetra_rp/cli/utils/skeleton_template/.flashignore" - "tetra_rp/cli/utils/skeleton_template/main.py" - "tetra_rp/cli/utils/skeleton_template/README.md" - "tetra_rp/cli/utils/skeleton_template/requirements.txt" - "tetra_rp/cli/utils/skeleton_template/workers/__init__.py" - "tetra_rp/cli/utils/skeleton_template/workers/cpu/__init__.py" - "tetra_rp/cli/utils/skeleton_template/workers/cpu/endpoint.py" - "tetra_rp/cli/utils/skeleton_template/workers/gpu/__init__.py" - "tetra_rp/cli/utils/skeleton_template/workers/gpu/endpoint.py" + "runpod_flash/cli/utils/skeleton_template/.env.example" + "runpod_flash/cli/utils/skeleton_template/.gitignore" + "runpod_flash/cli/utils/skeleton_template/.flashignore" + "runpod_flash/cli/utils/skeleton_template/main.py" + "runpod_flash/cli/utils/skeleton_template/README.md" + "runpod_flash/cli/utils/skeleton_template/requirements.txt" + "runpod_flash/cli/utils/skeleton_template/workers/__init__.py" + "runpod_flash/cli/utils/skeleton_template/workers/cpu/__init__.py" + "runpod_flash/cli/utils/skeleton_template/workers/cpu/endpoint.py" + "runpod_flash/cli/utils/skeleton_template/workers/gpu/__init__.py" + "runpod_flash/cli/utils/skeleton_template/workers/gpu/endpoint.py" ) MISSING_IN_WHEEL=0 diff --git a/scripts/verify-image-constants.sh b/scripts/verify-image-constants.sh index 3d727b14..a666d713 100755 --- a/scripts/verify-image-constants.sh +++ b/scripts/verify-image-constants.sh @@ -10,9 +10,9 @@ # This script can be retained and re-run to verify the fix after any changes. # It tests the following scenarios: # 1. Default behavior (no env vars set) - uses :latest tag -# 2. TETRA_IMAGE_TAG=local override - uses :local tag -# 3. TETRA_IMAGE_TAG=dev override - uses :dev tag -# 4. Individual TETRA_CPU_LB_IMAGE override +# 2. FLASH_IMAGE_TAG=local override - uses :local tag +# 3. FLASH_IMAGE_TAG=dev override - uses :dev tag +# 4. Individual FLASH_CPU_LB_IMAGE override # 5. Flash build integration test with environment variables # # Exit codes: @@ -76,7 +76,7 @@ EOF # Create gpu_worker.py with LiveServerless resource cat > "$TEST_PROJECT_DIR/gpu_worker.py" << 'EOF' -from tetra_rp import remote, LiveServerless +from runpod_flash import remote, LiveServerless gpu_config = LiveServerless(name="gpu_worker") @@ -87,7 +87,7 @@ EOF # Create mothership.py with explicit CpuLiveLoadBalancer cat > "$TEST_PROJECT_DIR/mothership.py" << 'EOF' -from tetra_rp import CpuLiveLoadBalancer +from runpod_flash import CpuLiveLoadBalancer mothership = CpuLiveLoadBalancer( name="test-mothership", @@ -114,7 +114,7 @@ EOF # Run flash build export PYTHONPATH="$REPO_ROOT/src:$PYTHONPATH" - python3 -m tetra_rp.cli.main build --no-docker --generate-file-structure 2>&1 | head -20 || true + python3 -m runpod_flash.cli.main build --no-docker --generate-file-structure 2>&1 | head -20 || true # Check the generated manifest if [ ! -f ".flash/flash_manifest.json" ]; then @@ -164,40 +164,40 @@ PYSCRIPT # Test 1: Default behavior (no env vars) echo "" echo -e "${YELLOW}Test Suite 1: Default Behavior${NC}" -unset TETRA_IMAGE_TAG -unset TETRA_CPU_LB_IMAGE -unset TETRA_LB_IMAGE +unset FLASH_IMAGE_TAG +unset FLASH_CPU_LB_IMAGE +unset FLASH_LB_IMAGE run_test "Default (should use :latest)" \ - "runpod/tetra-rp-lb-cpu:latest" \ - "runpod/tetra-rp:latest" \ + "runpod/flash-lb-cpu:latest" \ + "runpod/flash:latest" \ "" || exit 1 -# Test 2: TETRA_IMAGE_TAG=local +# Test 2: FLASH_IMAGE_TAG=local echo "" -echo -e "${YELLOW}Test Suite 2: TETRA_IMAGE_TAG Environment Variable${NC}" -unset TETRA_CPU_LB_IMAGE -unset TETRA_LB_IMAGE -run_test "TETRA_IMAGE_TAG=local" \ - "runpod/tetra-rp-lb-cpu:local" \ - "runpod/tetra-rp:local" \ - "TETRA_IMAGE_TAG=local" || exit 1 - -# Test 3: TETRA_IMAGE_TAG=dev -unset TETRA_CPU_LB_IMAGE -unset TETRA_LB_IMAGE -run_test "TETRA_IMAGE_TAG=dev" \ - "runpod/tetra-rp-lb-cpu:dev" \ - "runpod/tetra-rp:dev" \ - "TETRA_IMAGE_TAG=dev" || exit 1 +echo -e "${YELLOW}Test Suite 2: FLASH_IMAGE_TAG Environment Variable${NC}" +unset FLASH_CPU_LB_IMAGE +unset FLASH_LB_IMAGE +run_test "FLASH_IMAGE_TAG=local" \ + "runpod/flash-lb-cpu:local" \ + "runpod/flash:local" \ + "FLASH_IMAGE_TAG=local" || exit 1 + +# Test 3: FLASH_IMAGE_TAG=dev +unset FLASH_CPU_LB_IMAGE +unset FLASH_LB_IMAGE +run_test "FLASH_IMAGE_TAG=dev" \ + "runpod/flash-lb-cpu:dev" \ + "runpod/flash:dev" \ + "FLASH_IMAGE_TAG=dev" || exit 1 # Test 4: Individual image override echo "" echo -e "${YELLOW}Test Suite 3: Individual Image Overrides${NC}" -unset TETRA_IMAGE_TAG -run_test "Custom TETRA_CPU_LB_IMAGE" \ +unset FLASH_IMAGE_TAG +run_test "Custom FLASH_CPU_LB_IMAGE" \ "custom/lb-cpu:v1" \ - "runpod/tetra-rp:latest" \ - "TETRA_CPU_LB_IMAGE=custom/lb-cpu:v1" || exit 1 + "runpod/flash:latest" \ + "FLASH_CPU_LB_IMAGE=custom/lb-cpu:v1" || exit 1 # Test 5: Unit tests for constants echo "" @@ -216,34 +216,34 @@ import sys sys.path.insert(0, 'src') # Reset environment to test defaults -for var in ['TETRA_IMAGE_TAG', 'TETRA_GPU_IMAGE', 'TETRA_CPU_IMAGE', 'TETRA_LB_IMAGE', 'TETRA_CPU_LB_IMAGE']: +for var in ['FLASH_IMAGE_TAG', 'FLASH_GPU_IMAGE', 'FLASH_CPU_IMAGE', 'FLASH_LB_IMAGE', 'FLASH_CPU_LB_IMAGE']: if var in os.environ: del os.environ[var] -from tetra_rp.core.resources.constants import ( - TETRA_IMAGE_TAG, - TETRA_GPU_IMAGE, - TETRA_CPU_IMAGE, - TETRA_LB_IMAGE, - TETRA_CPU_LB_IMAGE, +from runpod_flash.core.resources.constants import ( + FLASH_IMAGE_TAG, + FLASH_GPU_IMAGE, + FLASH_CPU_IMAGE, + FLASH_LB_IMAGE, + FLASH_CPU_LB_IMAGE, DEFAULT_WORKERS_MIN, DEFAULT_WORKERS_MAX, ) -print(f"✓ TETRA_IMAGE_TAG: {TETRA_IMAGE_TAG}") -print(f"✓ TETRA_GPU_IMAGE: {TETRA_GPU_IMAGE}") -print(f"✓ TETRA_CPU_IMAGE: {TETRA_CPU_IMAGE}") -print(f"✓ TETRA_LB_IMAGE: {TETRA_LB_IMAGE}") -print(f"✓ TETRA_CPU_LB_IMAGE: {TETRA_CPU_LB_IMAGE}") +print(f"✓ FLASH_IMAGE_TAG: {FLASH_IMAGE_TAG}") +print(f"✓ FLASH_GPU_IMAGE: {FLASH_GPU_IMAGE}") +print(f"✓ FLASH_CPU_IMAGE: {FLASH_CPU_IMAGE}") +print(f"✓ FLASH_LB_IMAGE: {FLASH_LB_IMAGE}") +print(f"✓ FLASH_CPU_LB_IMAGE: {FLASH_CPU_LB_IMAGE}") print(f"✓ DEFAULT_WORKERS_MIN: {DEFAULT_WORKERS_MIN}") print(f"✓ DEFAULT_WORKERS_MAX: {DEFAULT_WORKERS_MAX}") # Verify defaults -assert TETRA_IMAGE_TAG == "latest", f"Expected 'latest', got {TETRA_IMAGE_TAG}" -assert TETRA_GPU_IMAGE == "runpod/tetra-rp:latest", f"Unexpected GPU image: {TETRA_GPU_IMAGE}" -assert TETRA_CPU_IMAGE == "runpod/tetra-rp-cpu:latest", f"Unexpected CPU image: {TETRA_CPU_IMAGE}" -assert TETRA_LB_IMAGE == "runpod/tetra-rp-lb:latest", f"Unexpected LB image: {TETRA_LB_IMAGE}" -assert TETRA_CPU_LB_IMAGE == "runpod/tetra-rp-lb-cpu:latest", f"Unexpected CPU LB image: {TETRA_CPU_LB_IMAGE}" +assert FLASH_IMAGE_TAG == "latest", f"Expected 'latest', got {FLASH_IMAGE_TAG}" +assert FLASH_GPU_IMAGE == "runpod/flash:latest", f"Unexpected GPU image: {FLASH_GPU_IMAGE}" +assert FLASH_CPU_IMAGE == "runpod/flash-cpu:latest", f"Unexpected CPU image: {FLASH_CPU_IMAGE}" +assert FLASH_LB_IMAGE == "runpod/flash-lb:latest", f"Unexpected LB image: {FLASH_LB_IMAGE}" +assert FLASH_CPU_LB_IMAGE == "runpod/flash-lb-cpu:latest", f"Unexpected CPU LB image: {FLASH_CPU_LB_IMAGE}" assert DEFAULT_WORKERS_MIN == 1 assert DEFAULT_WORKERS_MAX == 3 @@ -261,7 +261,7 @@ echo -e "${GREEN}========================================${NC}" echo "" echo "Summary:" echo " ✓ Default behavior uses :latest tag" -echo " ✓ TETRA_IMAGE_TAG environment variable works" +echo " ✓ FLASH_IMAGE_TAG environment variable works" echo " ✓ Individual image overrides work" echo " ✓ Constants are properly centralized" echo " ✓ Flash manifest generation uses constants" diff --git a/scripts/verify-manifest-constants.sh b/scripts/verify-manifest-constants.sh index fd180f6a..f3b7b702 100755 --- a/scripts/verify-manifest-constants.sh +++ b/scripts/verify-manifest-constants.sh @@ -70,7 +70,7 @@ test_scenario() { # Run flash build (with minimal verbosity) echo " Running: flash build --no-docker..." cd "$REPO_ROOT" - python3 -m tetra_rp.cli.commands.build build --no-docker --generate-file-structure 2>&1 > /dev/null || true + python3 -m runpod_flash.cli.commands.build build --no-docker --generate-file-structure 2>&1 > /dev/null || true # Check manifest in test directory if [ ! -f "$TEST_DIR/.flash/flash_manifest.json" ]; then @@ -108,22 +108,22 @@ PYTHON # Test 1: Default behavior (no env vars) echo -e "${YELLOW}Test Suite 1: Default Behavior${NC}" -(unset TETRA_IMAGE_TAG; test_scenario "Default (no env vars)" "" "runpod/tetra-rp-lb-cpu:latest") || true +(unset FLASH_IMAGE_TAG; test_scenario "Default (no env vars)" "" "runpod/flash-lb-cpu:latest") || true -# Test 2: With TETRA_IMAGE_TAG=local +# Test 2: With FLASH_IMAGE_TAG=local echo "" -echo -e "${YELLOW}Test Suite 2: With TETRA_IMAGE_TAG=local${NC}" -test_scenario "TETRA_IMAGE_TAG=local" "TETRA_IMAGE_TAG=local" "runpod/tetra-rp-lb-cpu:local" || true +echo -e "${YELLOW}Test Suite 2: With FLASH_IMAGE_TAG=local${NC}" +test_scenario "FLASH_IMAGE_TAG=local" "FLASH_IMAGE_TAG=local" "runpod/flash-lb-cpu:local" || true -# Test 3: With TETRA_IMAGE_TAG=dev +# Test 3: With FLASH_IMAGE_TAG=dev echo "" -echo -e "${YELLOW}Test Suite 3: With TETRA_IMAGE_TAG=dev${NC}" -test_scenario "TETRA_IMAGE_TAG=dev" "TETRA_IMAGE_TAG=dev" "runpod/tetra-rp-lb-cpu:dev" || true +echo -e "${YELLOW}Test Suite 3: With FLASH_IMAGE_TAG=dev${NC}" +test_scenario "FLASH_IMAGE_TAG=dev" "FLASH_IMAGE_TAG=dev" "runpod/flash-lb-cpu:dev" || true # Test 4: With custom CPU LB image echo "" echo -e "${YELLOW}Test Suite 4: Custom Image Override${NC}" -(unset TETRA_IMAGE_TAG; test_scenario "Custom TETRA_CPU_LB_IMAGE" "TETRA_CPU_LB_IMAGE=custom/lb-cpu:v1" "custom/lb-cpu:v1") || true +(unset FLASH_IMAGE_TAG; test_scenario "Custom FLASH_CPU_LB_IMAGE" "FLASH_CPU_LB_IMAGE=custom/lb-cpu:v1" "custom/lb-cpu:v1") || true # Test 5: Run the actual unit tests echo "" @@ -140,18 +140,18 @@ python3 << 'PYTHON' import sys sys.path.insert(0, 'src') -from tetra_rp.core.resources.constants import ( - TETRA_CPU_LB_IMAGE, - TETRA_LB_IMAGE, +from runpod_flash.core.resources.constants import ( + FLASH_CPU_LB_IMAGE, + FLASH_LB_IMAGE, DEFAULT_WORKERS_MIN, DEFAULT_WORKERS_MAX, ) -from tetra_rp.cli.commands.build_utils.manifest import ManifestBuilder +from runpod_flash.cli.commands.build_utils.manifest import ManifestBuilder from pathlib import Path print(" Verifying imports...") -print(f" ✓ TETRA_CPU_LB_IMAGE imported: {TETRA_CPU_LB_IMAGE}") -print(f" ✓ TETRA_LB_IMAGE imported: {TETRA_LB_IMAGE}") +print(f" ✓ FLASH_CPU_LB_IMAGE imported: {FLASH_CPU_LB_IMAGE}") +print(f" ✓ FLASH_LB_IMAGE imported: {FLASH_LB_IMAGE}") print(f" ✓ DEFAULT_WORKERS_MIN imported: {DEFAULT_WORKERS_MIN}") print(f" ✓ DEFAULT_WORKERS_MAX imported: {DEFAULT_WORKERS_MAX}") @@ -162,11 +162,11 @@ mothership = builder._create_mothership_resource({ "app_variable": "app" }) -assert mothership["imageName"] == TETRA_CPU_LB_IMAGE, f"Wrong image: {mothership['imageName']}" +assert mothership["imageName"] == FLASH_CPU_LB_IMAGE, f"Wrong image: {mothership['imageName']}" assert mothership["workersMin"] == DEFAULT_WORKERS_MIN, f"Wrong min workers: {mothership['workersMin']}" assert mothership["workersMax"] == DEFAULT_WORKERS_MAX, f"Wrong max workers: {mothership['workersMax']}" -print(f" ✓ Mothership uses TETRA_CPU_LB_IMAGE: {mothership['imageName']}") +print(f" ✓ Mothership uses FLASH_CPU_LB_IMAGE: {mothership['imageName']}") print(f" ✓ Mothership uses DEFAULT_WORKERS_MIN: {mothership['workersMin']}") print(f" ✓ Mothership uses DEFAULT_WORKERS_MAX: {mothership['workersMax']}") PYTHON @@ -202,8 +202,8 @@ if [ $FAILED -eq 0 ]; then echo "" echo "Summary of verified functionality:" echo " ✓ Default behavior uses :latest tag" - echo " ✓ TETRA_IMAGE_TAG environment variable works" - echo " ✓ Individual image overrides work (TETRA_CPU_LB_IMAGE, etc.)" + echo " ✓ FLASH_IMAGE_TAG environment variable works" + echo " ✓ Individual image overrides work (FLASH_CPU_LB_IMAGE, etc.)" echo " ✓ Constants are properly centralized in constants.py" echo " ✓ Manifest builder uses constants for mothership resources" echo " ✓ All unit tests pass" diff --git a/src/tetra_rp/__init__.py b/src/runpod_flash/__init__.py similarity index 100% rename from src/tetra_rp/__init__.py rename to src/runpod_flash/__init__.py diff --git a/src/tetra_rp/cli/__init__.py b/src/runpod_flash/cli/__init__.py similarity index 100% rename from src/tetra_rp/cli/__init__.py rename to src/runpod_flash/cli/__init__.py diff --git a/src/tetra_rp/cli/commands/__init__.py b/src/runpod_flash/cli/commands/__init__.py similarity index 100% rename from src/tetra_rp/cli/commands/__init__.py rename to src/runpod_flash/cli/commands/__init__.py diff --git a/src/tetra_rp/cli/commands/apps.py b/src/runpod_flash/cli/commands/apps.py similarity index 97% rename from src/tetra_rp/cli/commands/apps.py rename to src/runpod_flash/cli/commands/apps.py index 9f8efe77..464fd4a1 100644 --- a/src/tetra_rp/cli/commands/apps.py +++ b/src/runpod_flash/cli/commands/apps.py @@ -6,10 +6,10 @@ from rich.panel import Panel import asyncio -from tetra_rp.cli.utils.app import discover_flash_project +from runpod_flash.cli.utils.app import discover_flash_project -from tetra_rp.core.resources.app import FlashApp +from runpod_flash.core.resources.app import FlashApp console = Console() diff --git a/src/tetra_rp/cli/commands/build.py b/src/runpod_flash/cli/commands/build.py similarity index 91% rename from src/tetra_rp/cli/commands/build.py rename to src/runpod_flash/cli/commands/build.py index 9f96acf6..80615193 100644 --- a/src/tetra_rp/cli/commands/build.py +++ b/src/runpod_flash/cli/commands/build.py @@ -57,14 +57,14 @@ PIP_MODULE = "pip" -def _find_local_tetra_rp() -> Optional[Path]: - """Find local tetra_rp source directory if available. +def _find_local_runpod_flash() -> Optional[Path]: + """Find local runpod_flash source directory if available. Returns: - Path to tetra_rp package directory, or None if not found or installed from PyPI + Path to runpod_flash package directory, or None if not found or installed from PyPI """ try: - spec = importlib.util.find_spec("tetra_rp") + spec = importlib.util.find_spec("runpod_flash") if not spec or not spec.origin: return None @@ -83,8 +83,8 @@ def _find_local_tetra_rp() -> Optional[Path]: return None -def _bundle_local_tetra_rp(build_dir: Path) -> bool: - """Copy local tetra_rp source into build directory. +def _bundle_local_runpod_flash(build_dir: Path) -> bool: + """Copy local runpod_flash source into build directory. Args: build_dir: Target build directory @@ -92,50 +92,50 @@ def _bundle_local_tetra_rp(build_dir: Path) -> bool: Returns: True if bundled successfully, False otherwise """ - tetra_pkg = _find_local_tetra_rp() + flash_pkg = _find_local_runpod_flash() - if not tetra_pkg: + if not flash_pkg: console.print( - "[yellow]⚠ Local tetra_rp not found or using PyPI install[/yellow]" + "[yellow]⚠ Local runpod_flash not found or using PyPI install[/yellow]" ) return False - # Copy tetra_rp to build - dest = build_dir / "tetra_rp" + # Copy runpod_flash to build + dest = build_dir / "runpod_flash" if dest.exists(): shutil.rmtree(dest) shutil.copytree( - tetra_pkg, + flash_pkg, dest, ignore=shutil.ignore_patterns("__pycache__", "*.pyc", ".pytest_cache"), ) - console.print(f"[cyan]✓ Bundled local tetra_rp from {tetra_pkg}[/cyan]") + console.print(f"[cyan]✓ Bundled local runpod_flash from {flash_pkg}[/cyan]") return True -def _extract_tetra_rp_dependencies(tetra_pkg_dir: Path) -> list[str]: - """Extract runtime dependencies from tetra_rp's pyproject.toml. +def _extract_runpod_flash_dependencies(flash_pkg_dir: Path) -> list[str]: + """Extract runtime dependencies from runpod_flash's pyproject.toml. - When bundling local tetra_rp source, we need to also install its dependencies + When bundling local runpod_flash source, we need to also install its dependencies so they're available in the build environment. Args: - tetra_pkg_dir: Path to tetra_rp package directory (src/tetra_rp) + flash_pkg_dir: Path to runpod_flash package directory (src/runpod_flash) Returns: List of dependency strings, empty list if parsing fails """ try: - # Navigate from tetra_rp package to project root - # tetra_pkg_dir is src/tetra_rp, need to go up 2 levels to reach project root - project_root = tetra_pkg_dir.parent.parent + # Navigate from runpod_flash package to project root + # flash_pkg_dir is src/runpod_flash, need to go up 2 levels to reach project root + project_root = flash_pkg_dir.parent.parent pyproject_path = project_root / "pyproject.toml" if not pyproject_path.exists(): console.print( - "[yellow]⚠ tetra_rp pyproject.toml not found, " + "[yellow]⚠ runpod_flash pyproject.toml not found, " "dependencies may be missing[/yellow]" ) return [] @@ -149,18 +149,20 @@ def _extract_tetra_rp_dependencies(tetra_pkg_dir: Path) -> list[str]: if dependencies: console.print( - f"[dim]Found {len(dependencies)} tetra_rp dependencies to install[/dim]" + f"[dim]Found {len(dependencies)} runpod_flash dependencies to install[/dim]" ) return dependencies except Exception as e: - console.print(f"[yellow]⚠ Failed to parse tetra_rp dependencies: {e}[/yellow]") + console.print( + f"[yellow]⚠ Failed to parse runpod_flash dependencies: {e}[/yellow]" + ) return [] -def _remove_tetra_from_requirements(build_dir: Path) -> None: - """Remove tetra_rp from requirements.txt and clean up dist-info since we bundled source.""" +def _remove_runpod_flash_from_requirements(build_dir: Path) -> None: + """Remove runpod_flash from requirements.txt and clean up dist-info since we bundled source.""" req_file = build_dir / "requirements.txt" if not req_file.exists(): @@ -170,15 +172,15 @@ def _remove_tetra_from_requirements(build_dir: Path) -> None: filtered = [ line for line in lines - if not line.strip().startswith("tetra_rp") - and not line.strip().startswith("tetra-rp") + if not line.strip().startswith("runpod_flash") + and not line.strip().startswith("runpod-flash") ] req_file.write_text("\n".join(filtered) + "\n") - # Remove tetra_rp dist-info directory to avoid conflicts with bundled source + # Remove runpod_flash dist-info directory to avoid conflicts with bundled source # dist-info is created by pip install and can confuse Python's import system - for dist_info in build_dir.glob("tetra_rp-*.dist-info"): + for dist_info in build_dir.glob("runpod_flash-*.dist-info"): if dist_info.is_dir(): shutil.rmtree(dist_info) @@ -198,10 +200,10 @@ def build_command( "--exclude", help="Comma-separated packages to exclude (e.g., 'torch,torchvision')", ), - use_local_tetra: bool = typer.Option( + use_local_flash: bool = typer.Option( False, - "--use-local-tetra", - help="Bundle local tetra_rp source instead of PyPI version (for development/testing)", + "--use-local-flash", + help="Bundle local runpod_flash source instead of PyPI version (for development/testing)", ), preview: bool = typer.Option( False, @@ -353,20 +355,20 @@ def build_command( logger.exception("Build failed") raise typer.Exit(1) - # Extract tetra_rp dependencies if bundling local version - tetra_deps = [] - if use_local_tetra: - tetra_pkg = _find_local_tetra_rp() - if tetra_pkg: - tetra_deps = _extract_tetra_rp_dependencies(tetra_pkg) + # Extract runpod_flash dependencies if bundling local version + flash_deps = [] + if use_local_flash: + flash_pkg = _find_local_runpod_flash() + if flash_pkg: + flash_deps = _extract_runpod_flash_dependencies(flash_pkg) # Install dependencies deps_task = progress.add_task("Installing dependencies...") requirements = collect_requirements(project_dir, build_dir) - # Add tetra_rp dependencies if bundling local version - # This ensures all tetra_rp runtime dependencies are available in the build - requirements.extend(tetra_deps) + # Add runpod_flash dependencies if bundling local version + # This ensures all runpod_flash runtime dependencies are available in the build + requirements.extend(flash_deps) # Filter out excluded packages if excluded_packages: @@ -425,21 +427,21 @@ def build_command( progress.stop_task(deps_task) - # Bundle local tetra_rp if requested - if use_local_tetra: - tetra_task = progress.add_task("Bundling local tetra_rp...") - if _bundle_local_tetra_rp(build_dir): - _remove_tetra_from_requirements(build_dir) + # Bundle local runpod_flash if requested + if use_local_flash: + flash_task = progress.add_task("Bundling local runpod_flash...") + if _bundle_local_runpod_flash(build_dir): + _remove_runpod_flash_from_requirements(build_dir) progress.update( - tetra_task, - description="[green]✓ Bundled local tetra_rp", + flash_task, + description="[green]✓ Bundled local runpod_flash", ) else: progress.update( - tetra_task, - description="[yellow]⚠ Using PyPI tetra_rp", + flash_task, + description="[yellow]⚠ Using PyPI runpod_flash", ) - progress.stop_task(tetra_task) + progress.stop_task(flash_task) # Clean up Python bytecode before archiving cleanup_python_bytecode(build_dir) diff --git a/src/tetra_rp/cli/commands/build_utils/__init__.py b/src/runpod_flash/cli/commands/build_utils/__init__.py similarity index 100% rename from src/tetra_rp/cli/commands/build_utils/__init__.py rename to src/runpod_flash/cli/commands/build_utils/__init__.py diff --git a/src/runpod_flash/cli/commands/build_utils/handler_generator.py b/src/runpod_flash/cli/commands/build_utils/handler_generator.py new file mode 100644 index 00000000..9759a7fb --- /dev/null +++ b/src/runpod_flash/cli/commands/build_utils/handler_generator.py @@ -0,0 +1,176 @@ +"""Generator for handler_.py files.""" + +import importlib +import importlib.util +import logging +from pathlib import Path +from typing import Any, Dict, List, Union + +from runpod_flash.runtime.models import Manifest + +logger = logging.getLogger(__name__) + +HANDLER_TEMPLATE = '''""" +Auto-generated handler for resource: {resource_name} +Generated at: {timestamp} + +This file is generated by the Flash build process. Do not edit manually. +""" + +import importlib +from runpod_flash.runtime.generic_handler import create_handler + +# Import all functions/classes that belong to this resource +{imports} + +# Function registry for this handler +FUNCTION_REGISTRY = {{ +{registry} +}} + +# Create configured handler +handler = create_handler(FUNCTION_REGISTRY) + +if __name__ == "__main__": + import runpod + runpod.serverless.start({{"handler": handler}}) +''' + + +class HandlerGenerator: + """Generates handler_.py files for each resource config.""" + + def __init__(self, manifest: Union[Dict[str, Any], Manifest], build_dir: Path): + self.manifest = manifest + self.build_dir = build_dir + + def generate_handlers(self) -> List[Path]: + """Generate all handler files for queue-based (non-LB) resources.""" + handler_paths = [] + + # Handle both dict and Manifest types + resources = ( + self.manifest.resources + if isinstance(self.manifest, Manifest) + else self.manifest.get("resources", {}) + ) + + for resource_name, resource_data in resources.items(): + # Skip load-balanced resources (handled by LBHandlerGenerator) + # Use flag determined by isinstance() at scan time + is_load_balanced = ( + resource_data.is_load_balanced + if hasattr(resource_data, "is_load_balanced") + else resource_data.get("is_load_balanced", False) + ) + if is_load_balanced: + continue + + handler_path = self._generate_handler(resource_name, resource_data) + handler_paths.append(handler_path) + + return handler_paths + + def _generate_handler(self, resource_name: str, resource_data: Any) -> Path: + """Generate a single handler file.""" + handler_filename = f"handler_{resource_name}.py" + handler_path = self.build_dir / handler_filename + + # Get timestamp from manifest + timestamp = ( + self.manifest.generated_at + if isinstance(self.manifest, Manifest) + else self.manifest.get("generated_at", "") + ) + + # Get functions from resource (handle both dict and ResourceConfig) + functions = ( + resource_data.functions + if hasattr(resource_data, "functions") + else resource_data.get("functions", []) + ) + + # Generate imports section + imports = self._generate_imports(functions) + + # Generate function registry + registry = self._generate_registry(functions) + + # Format template + handler_code = HANDLER_TEMPLATE.format( + resource_name=resource_name, + timestamp=timestamp, + imports=imports, + registry=registry, + ) + + handler_path.write_text(handler_code) + + # Validate that generated handler can be imported + self._validate_handler_imports(handler_path) + + return handler_path + + def _generate_imports(self, functions: List[Any]) -> str: + """Generate import statements for functions using dynamic imports. + + Uses importlib.import_module() to handle module names with invalid + Python identifiers (e.g., names starting with digits like '01_hello_world'). + """ + if not functions: + return "# No functions to import" + + imports = [] + for func in functions: + # Handle both dict and FunctionMetadata + module = func.module if hasattr(func, "module") else func.get("module") + name = func.name if hasattr(func, "name") else func.get("name") + + if module and name: + # Use dynamic import to handle invalid identifiers + imports.append(f"{name} = importlib.import_module('{module}').{name}") + + return "\n".join(imports) if imports else "# No functions to import" + + def _generate_registry(self, functions: List[Any]) -> str: + """Generate function registry dictionary.""" + if not functions: + return " # No functions registered" + + registry_lines = [] + + for func in functions: + # Handle both dict and FunctionMetadata + name = func.name if hasattr(func, "name") else func.get("name") + registry_lines.append(f' "{name}": {name},') + + return "\n".join(registry_lines) + + def _validate_handler_imports(self, handler_path: Path) -> None: + """Validate that generated handler has valid Python syntax. + + Attempts to load the handler module to catch syntax errors. + ImportErrors for missing worker modules are logged but not fatal, + as those imports may not be available at build time. + + Args: + handler_path: Path to generated handler file + + Raises: + ValueError: If handler has syntax errors or cannot be parsed + """ + try: + spec = importlib.util.spec_from_file_location("handler", handler_path) + if spec and spec.loader: + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + else: + raise ValueError("Failed to create module spec") + except SyntaxError as e: + raise ValueError(f"Handler has syntax errors: {e}") from e + except ImportError as e: + # Log but don't fail - imports might not be available at build time + logger.debug(f"Handler import validation: {e}") + except Exception as e: + # Only raise for truly unexpected errors + logger.warning(f"Handler validation warning: {e}") diff --git a/src/runpod_flash/cli/commands/build_utils/lb_handler_generator.py b/src/runpod_flash/cli/commands/build_utils/lb_handler_generator.py new file mode 100644 index 00000000..dcd0845d --- /dev/null +++ b/src/runpod_flash/cli/commands/build_utils/lb_handler_generator.py @@ -0,0 +1,309 @@ +"""Generator for FastAPI handlers for LoadBalancerSlsResource endpoints.""" + +import importlib.util +import logging +from pathlib import Path +from typing import Any, Dict, List, Union + +from runpod_flash.runtime.models import Manifest + +logger = logging.getLogger(__name__) + +LB_HANDLER_TEMPLATE = '''""" +Auto-generated FastAPI handler for LoadBalancerSlsResource: {resource_name} +Generated at: {timestamp} + +This file is generated by the Flash build process. Do not edit manually. + +Load-balanced endpoints expose HTTP servers directly to clients, enabling: +- REST APIs with custom HTTP routing +- WebSocket servers +- Real-time communication patterns +""" + +import asyncio +import logging +from contextlib import asynccontextmanager +from pathlib import Path +from typing import Optional + +from fastapi import FastAPI, Request +from runpod_flash.runtime.lb_handler import create_lb_handler + +logger = logging.getLogger(__name__) + +# Import all functions/classes that belong to this resource +{imports} + +# Route registry: (method, path) -> function +ROUTE_REGISTRY = {{ +{registry} +}} + + +# Lifespan context manager for startup/shutdown +@asynccontextmanager +async def lifespan(app: FastAPI): + """Handle application startup and shutdown.""" + # Startup + logger.info("Starting {resource_name} endpoint") + + # Check if this is the mothership and run reconciliation + # Note: Resources are now provisioned upfront by the CLI during deployment. + # This background task runs reconciliation on mothership startup to ensure + # all resources are still deployed and in sync with the manifest. + try: + from runpod_flash.runtime.mothership_provisioner import ( + is_mothership, + reconcile_children, + get_mothership_url, + ) + from runpod_flash.runtime.state_manager_client import StateManagerClient + + if is_mothership(): + logger.info("=" * 60) + logger.info("Mothership detected - Starting reconciliation task") + logger.info("Resources are provisioned upfront by the CLI") + logger.info("This task ensures all resources remain in sync") + logger.info("=" * 60) + try: + mothership_url = get_mothership_url() + logger.info(f"Mothership URL: {{mothership_url}}") + + # Initialize State Manager client for reconciliation + state_client = StateManagerClient() + + # Spawn background reconciliation task (non-blocking) + # This will verify all resources from manifest are deployed + manifest_path = Path(__file__).parent / "flash_manifest.json" + task = asyncio.create_task( + reconcile_children(manifest_path, mothership_url, state_client) + ) + # Add error callback to catch and log background task exceptions + task.add_done_callback( + lambda t: logger.error(f"Reconciliation task failed: {{t.exception()}}") + if t.exception() + else None + ) + + except Exception as e: + logger.error(f"Failed to start reconciliation task: {{e}}") + # Don't fail startup - continue serving traffic + + except ImportError: + logger.debug("Mothership provisioning modules not available") + + yield + + # Shutdown + logger.info("Shutting down {resource_name} endpoint") + + +# Create FastAPI app with routes and lifespan +# Note: include_execute={include_execute} for this endpoint type +# - LiveLoadBalancer (local): include_execute=True for /execute endpoint +# - LoadBalancerSlsResource (deployed): include_execute=False (security) +app = create_lb_handler(ROUTE_REGISTRY, include_execute={include_execute}, lifespan=lifespan) + + +# Health check endpoint (required for RunPod load-balancer endpoints) +@app.get("/ping") +def ping(): + """Health check endpoint for RunPod load-balancer. + + Returns: + dict: Status response + """ + return {{"status": "healthy"}} + + +if __name__ == "__main__": + import uvicorn + # Local development server for testing + uvicorn.run(app, host="0.0.0.0", port=8000) +''' + + +class LBHandlerGenerator: + """Generates FastAPI handlers for LoadBalancerSlsResource endpoints.""" + + def __init__(self, manifest: Union[Dict[str, Any], Manifest], build_dir: Path): + self.manifest = manifest + self.build_dir = build_dir + + def generate_handlers(self) -> List[Path]: + """Generate all LB handler files.""" + handler_paths = [] + + # Handle both dict and Manifest types + resources = ( + self.manifest.resources + if isinstance(self.manifest, Manifest) + else self.manifest.get("resources", {}) + ) + + for resource_name, resource_data in resources.items(): + # Generate for both LiveLoadBalancer (local dev) and LoadBalancerSlsResource (deployed) + # Use flag determined by isinstance() at scan time + is_load_balanced = ( + resource_data.is_load_balanced + if hasattr(resource_data, "is_load_balanced") + else resource_data.get("is_load_balanced", False) + ) + if not is_load_balanced: + continue + + handler_path = self._generate_handler(resource_name, resource_data) + handler_paths.append(handler_path) + + return handler_paths + + def _generate_handler(self, resource_name: str, resource_data: Any) -> Path: + """Generate a single FastAPI handler file.""" + handler_filename = f"handler_{resource_name}.py" + handler_path = self.build_dir / handler_filename + + # Get timestamp from manifest + timestamp = ( + self.manifest.generated_at + if isinstance(self.manifest, Manifest) + else self.manifest.get("generated_at", "") + ) + + # Determine if /execute endpoint should be included + # LiveLoadBalancer (local dev) includes /execute, deployed LoadBalancerSlsResource does not + # Use flag determined by isinstance() at scan time + include_execute = ( + resource_data.is_live_resource + if hasattr(resource_data, "is_live_resource") + else resource_data.get("is_live_resource", False) + ) + + # Get functions from resource (handle both dict and ResourceConfig) + functions = ( + resource_data.functions + if hasattr(resource_data, "functions") + else resource_data.get("functions", []) + ) + + # Generate imports section + imports = self._generate_imports(functions) + + # Generate route registry + registry = self._generate_route_registry(functions) + + # Format template + handler_code = LB_HANDLER_TEMPLATE.format( + resource_name=resource_name, + timestamp=timestamp, + imports=imports, + registry=registry, + include_execute=str(include_execute), + ) + + handler_path.write_text(handler_code) + + # Validate that generated handler can be imported + self._validate_handler_imports(handler_path) + + return handler_path + + def _generate_imports(self, functions: List[Any]) -> str: + """Generate import statements for functions. + + Uses importlib to handle module paths with any characters, + including numeric prefixes that aren't valid Python identifiers. + + Args: + functions: List of function metadata (dicts or FunctionMetadata objects) + + Returns: + Import statements as string + """ + if not functions: + return "# No functions to import" + + imports = ["import importlib"] + + for func in functions: + # Handle both dict and FunctionMetadata + module = func.module if hasattr(func, "module") else func.get("module") + name = func.name if hasattr(func, "name") else func.get("name") + + if module and name: + # Use importlib to handle module names with invalid identifiers + imports.append(f"{name} = importlib.import_module('{module}').{name}") + + return "\n".join(imports) + + def _generate_route_registry(self, functions: List[Any]) -> str: + """Generate route registry for FastAPI app. + + Creates mapping of (method, path) tuples to function names. + + Args: + functions: List of function metadata dicts with http_method and http_path + + Returns: + Registry dictionary as string + """ + if not functions: + return " # No functions registered" + + registry_lines = [] + + for func in functions: + # Handle both dict and FunctionMetadata + name = func.name if hasattr(func, "name") else func.get("name") + method = ( + func.http_method + if hasattr(func, "http_method") + else func.get("http_method") + ) + path = ( + func.http_path if hasattr(func, "http_path") else func.get("http_path") + ) + + if name and method and path: + # Create tuple key: ("GET", "/api/process") + registry_lines.append(f' ("{method}", "{path}"): {name},') + elif name: + # Skip if method or path missing (shouldn't happen with validation) + logger.warning( + f"Function '{name}' missing http_method or http_path. Skipping." + ) + + return ( + "\n".join(registry_lines) + if registry_lines + else " # No routes registered" + ) + + def _validate_handler_imports(self, handler_path: Path) -> None: + """Validate that generated handler has valid Python syntax. + + Attempts to load the handler module to catch syntax errors. + ImportErrors for missing worker modules are logged but not fatal, + as those imports may not be available at build time. + + Args: + handler_path: Path to generated handler file + + Raises: + ValueError: If handler has syntax errors or cannot be parsed + """ + try: + spec = importlib.util.spec_from_file_location("handler", handler_path) + if spec and spec.loader: + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + else: + raise ValueError("Failed to create module spec") + except SyntaxError as e: + raise ValueError(f"Handler has syntax errors: {e}") from e + except ImportError as e: + # Log but don't fail - imports might not be available at build time + logger.debug(f"Handler import validation: {e}") + except Exception as e: + # Only raise for truly unexpected errors + logger.warning(f"Handler validation warning: {e}") diff --git a/src/tetra_rp/cli/commands/build_utils/manifest.py b/src/runpod_flash/cli/commands/build_utils/manifest.py similarity index 98% rename from src/tetra_rp/cli/commands/build_utils/manifest.py rename to src/runpod_flash/cli/commands/build_utils/manifest.py index 44b354b1..f78f968f 100644 --- a/src/tetra_rp/cli/commands/build_utils/manifest.py +++ b/src/runpod_flash/cli/commands/build_utils/manifest.py @@ -9,11 +9,11 @@ from pathlib import Path from typing import Any, Dict, List, Optional -from tetra_rp.core.resources.constants import ( +from runpod_flash.core.resources.constants import ( DEFAULT_WORKERS_MAX, DEFAULT_WORKERS_MIN, - TETRA_CPU_LB_IMAGE, - TETRA_LB_IMAGE, + FLASH_CPU_LB_IMAGE, + FLASH_LB_IMAGE, ) from .scanner import RemoteFunctionMetadata, detect_explicit_mothership, detect_main_app @@ -205,7 +205,7 @@ def _create_mothership_resource(self, main_app_config: dict) -> Dict[str, Any]: "is_mothership": True, "main_file": main_app_config["file_path"].name, "app_variable": main_app_config["app_variable"], - "imageName": TETRA_CPU_LB_IMAGE, + "imageName": FLASH_CPU_LB_IMAGE, "workersMin": DEFAULT_WORKERS_MIN, "workersMax": DEFAULT_WORKERS_MAX, } @@ -236,9 +236,9 @@ def _create_mothership_from_explicit( # Map resource type to image name resource_type = explicit_config.get("resource_type", "CpuLiveLoadBalancer") if resource_type == "LiveLoadBalancer": - image_name = TETRA_LB_IMAGE # GPU load balancer + image_name = FLASH_LB_IMAGE # GPU load balancer else: - image_name = TETRA_CPU_LB_IMAGE # CPU load balancer + image_name = FLASH_CPU_LB_IMAGE # CPU load balancer return { "resource_type": resource_type, diff --git a/src/runpod_flash/cli/commands/build_utils/mothership_handler_generator.py b/src/runpod_flash/cli/commands/build_utils/mothership_handler_generator.py new file mode 100644 index 00000000..89e6030a --- /dev/null +++ b/src/runpod_flash/cli/commands/build_utils/mothership_handler_generator.py @@ -0,0 +1,75 @@ +"""Generator for mothership handler that serves main.py FastAPI app.""" + +import logging +from pathlib import Path + +logger = logging.getLogger(__name__) + +MOTHERSHIP_HANDLER_TEMPLATE = '''"""Auto-generated handler for mothership endpoint.""" + +import os +from fastapi.middleware.cors import CORSMiddleware +from {main_module} import {app_variable} as app + +# Add CORS middleware for cross-origin requests +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Mothership endpoint information (set by RunPod when deployed) +MOTHERSHIP_ID = os.getenv("RUNPOD_ENDPOINT_ID") +MOTHERSHIP_URL = os.getenv("RUNPOD_ENDPOINT_URL") + + +@app.get("/ping") +async def ping(): + """Health check endpoint required by RunPod.""" + return {{"status": "healthy", "endpoint": "mothership", "id": MOTHERSHIP_ID}} + + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) +''' + + +def generate_mothership_handler( + main_file: str, + app_variable: str, + output_path: Path, +) -> None: + """Generate handler that imports and serves user's main.py FastAPI app. + + Args: + main_file: Filename of main.py (e.g., "main.py") + app_variable: Name of the FastAPI app variable (e.g., "app") + output_path: Path where to write the generated handler file + + Raises: + ValueError: If parameters are invalid + """ + if not main_file or not main_file.endswith(".py"): + raise ValueError(f"Invalid main_file: {main_file}") + + if not app_variable or not app_variable.isidentifier(): + raise ValueError(f"Invalid app_variable: {app_variable}") + + # Convert filename to module name (e.g., "main.py" -> "main") + main_module = main_file.replace(".py", "") + + # Generate handler code + handler_code = MOTHERSHIP_HANDLER_TEMPLATE.format( + main_module=main_module, + app_variable=app_variable, + ) + + # Ensure output directory exists + output_path.parent.mkdir(parents=True, exist_ok=True) + + # Write handler file + output_path.write_text(handler_code) + logger.info(f"Generated mothership handler: {output_path}") diff --git a/src/tetra_rp/cli/commands/build_utils/scanner.py b/src/runpod_flash/cli/commands/build_utils/scanner.py similarity index 99% rename from src/tetra_rp/cli/commands/build_utils/scanner.py rename to src/runpod_flash/cli/commands/build_utils/scanner.py index 4faf0581..910d3a25 100644 --- a/src/tetra_rp/cli/commands/build_utils/scanner.py +++ b/src/runpod_flash/cli/commands/build_utils/scanner.py @@ -282,10 +282,10 @@ def _is_resource_config_type(self, type_name: str) -> bool: Returns True only if the class can be imported and is a ServerlessResource. """ - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.core.resources.serverless import ServerlessResource try: - module = importlib.import_module("tetra_rp") + module = importlib.import_module("runpod_flash") if hasattr(module, type_name): cls = getattr(module, type_name) return isinstance(cls, type) and issubclass(cls, ServerlessResource) diff --git a/src/tetra_rp/cli/commands/deploy.py b/src/runpod_flash/cli/commands/deploy.py similarity index 99% rename from src/tetra_rp/cli/commands/deploy.py rename to src/runpod_flash/cli/commands/deploy.py index 7a457e88..93e9e271 100644 --- a/src/tetra_rp/cli/commands/deploy.py +++ b/src/runpod_flash/cli/commands/deploy.py @@ -19,13 +19,13 @@ from ..utils.app import discover_flash_project -from tetra_rp.core.resources.app import FlashApp +from runpod_flash.core.resources.app import FlashApp console = Console() def _get_resource_manager(): - from tetra_rp.core.resources.resource_manager import ResourceManager + from runpod_flash.core.resources.resource_manager import ResourceManager return ResourceManager() diff --git a/src/tetra_rp/cli/commands/init.py b/src/runpod_flash/cli/commands/init.py similarity index 100% rename from src/tetra_rp/cli/commands/init.py rename to src/runpod_flash/cli/commands/init.py diff --git a/src/tetra_rp/cli/commands/preview.py b/src/runpod_flash/cli/commands/preview.py similarity index 98% rename from src/tetra_rp/cli/commands/preview.py rename to src/runpod_flash/cli/commands/preview.py index 73b12e72..cf4866c1 100644 --- a/src/tetra_rp/cli/commands/preview.py +++ b/src/runpod_flash/cli/commands/preview.py @@ -12,7 +12,7 @@ from rich.console import Console from rich.table import Table -from tetra_rp.core.resources.constants import TETRA_CPU_LB_IMAGE +from runpod_flash.core.resources.constants import FLASH_CPU_LB_IMAGE logger = logging.getLogger(__name__) console = Console() @@ -174,7 +174,7 @@ def _parse_resources_from_manifest(manifest: dict) -> dict: for resource_name, resource_data in manifest_resources.items(): resources[resource_name] = { "is_mothership": resource_data.get("is_mothership", False), - "imageName": resource_data.get("imageName", TETRA_CPU_LB_IMAGE), + "imageName": resource_data.get("imageName", FLASH_CPU_LB_IMAGE), "functions": resource_data.get("functions", []), } @@ -183,7 +183,7 @@ def _parse_resources_from_manifest(manifest: dict) -> dict: if not has_mothership: resources["mothership"] = { "is_mothership": True, - "imageName": TETRA_CPU_LB_IMAGE, + "imageName": FLASH_CPU_LB_IMAGE, } return resources @@ -235,7 +235,7 @@ def _start_resource_container( Exception: If container startup fails """ # Determine Docker image - image = resource_config.get("imageName", TETRA_CPU_LB_IMAGE) + image = resource_config.get("imageName", FLASH_CPU_LB_IMAGE) is_mothership = resource_config.get("is_mothership", False) # Assign port diff --git a/src/tetra_rp/cli/commands/resource.py b/src/runpod_flash/cli/commands/resource.py similarity index 97% rename from src/tetra_rp/cli/commands/resource.py rename to src/runpod_flash/cli/commands/resource.py index c39cd659..6bc28739 100644 --- a/src/tetra_rp/cli/commands/resource.py +++ b/src/runpod_flash/cli/commands/resource.py @@ -48,7 +48,7 @@ def generate_resource_table(resource_manager: ResourceManager) -> Panel: if not resources: return Panel( "📊 No resources currently tracked\n\n" - "Resources will appear here after running your Tetra applications.", + "Resources will appear here after running your Flash applications.", title="Resource Status Report", expand=False, ) diff --git a/src/tetra_rp/cli/commands/run.py b/src/runpod_flash/cli/commands/run.py similarity index 100% rename from src/tetra_rp/cli/commands/run.py rename to src/runpod_flash/cli/commands/run.py diff --git a/src/runpod_flash/cli/commands/test_mothership.py b/src/runpod_flash/cli/commands/test_mothership.py new file mode 100644 index 00000000..4e7b66bd --- /dev/null +++ b/src/runpod_flash/cli/commands/test_mothership.py @@ -0,0 +1,458 @@ +"""Flash test-mothership command - Test mothership boot locally with Docker.""" + +import logging +import shutil +import subprocess +import sys +import time +from pathlib import Path +from typing import Optional + +import typer +from rich.console import Console +from rich.panel import Panel + +logger = logging.getLogger(__name__) +console = Console() + + +def _clear_resource_cache() -> None: + """Clear ResourceManager cache for clean test environment. + + Test-mothership deploys temporary endpoints that should not persist + between test runs. Clearing the cache prevents: + - Stale resources from previous tests being redeployed + - Name conflicts between old and new test resources + - Confusion from endpoints that no longer exist in the codebase + """ + cache_file = Path.home() / ".runpod" / "resources.pkl" + if cache_file.exists(): + try: + cache_file.unlink() + console.print( + "[dim]Cleared resource cache for clean test environment[/dim]" + ) + logger.debug(f"Removed cache file: {cache_file}") + except Exception as e: + console.print(f"[yellow]Warning: Could not clear cache: {e}[/yellow]") + logger.warning(f"Failed to remove cache file {cache_file}: {e}") + + +def test_mothership_command( + image: str = typer.Option( + "runpod/flash-lb-cpu:local", + "--image", + help="Docker image to use for testing", + ), + port: int = typer.Option(8000, "--port", help="Local port to expose"), + endpoint_id: Optional[str] = typer.Option( + None, "--endpoint-id", help="RunPod endpoint ID (auto-generated if omitted)" + ), + build_dir: str = typer.Option( + ".flash/.build", "--build-dir", help="Path to build directory" + ), + no_build: bool = typer.Option( + False, "--no-build", help="Skip running flash build first" + ), +): + """ + Test mothership boot locally with Docker. + + Runs the application in a Docker container with mothership provisioning enabled. + This simulates the mothership deployment process, including auto-provisioning of + child resources to RunPod. On shutdown (Ctrl+C or docker stop), automatically + cleans up all deployed endpoints. + + Examples: + flash test-mothership # Default setup + flash test-mothership --port 9000 # Custom port + flash test-mothership --image custom:latest # Custom Docker image + flash test-mothership --no-build # Skip flash build step + """ + try: + # Verify prerequisites + _verify_prerequisites() + + # Clear resource cache to prevent stale entries in test mode + _clear_resource_cache() + + # Build if needed + if not no_build: + _run_flash_build() + + # Generate endpoint ID if not provided + if not endpoint_id: + endpoint_id = f"test-mothership-{int(time.time())}" + + # Create entrypoint script for cleanup on shutdown + _create_entrypoint_script(build_dir) + + # Display configuration + _display_test_objectives() + _display_config(build_dir, image, port, endpoint_id) + + # Build Docker command + docker_cmd = _build_docker_command(image, port, endpoint_id, build_dir) + + # Run Docker container + _run_docker_container(docker_cmd, port) + + except typer.Exit: + raise + except Exception as e: + console.print(f"[red]Error:[/red] {e}") + logger.exception("Unexpected error in test_mothership_command") + raise typer.Exit(1) + + +def _verify_prerequisites() -> None: + """Verify that Docker and RUNPOD_API_KEY are available.""" + # Check Docker + result = shutil.which("docker") + if not result: + console.print("[red]Error:[/red] Docker is not installed or not in PATH") + console.print( + "Install Docker from: https://www.docker.com/products/docker-desktop" + ) + raise typer.Exit(1) + + # Check Docker daemon + try: + subprocess.run( + ["docker", "ps"], + capture_output=True, + check=True, + timeout=5, + ) + except ( + subprocess.CalledProcessError, + subprocess.TimeoutExpired, + FileNotFoundError, + ): + console.print("[red]Error:[/red] Docker daemon is not running") + console.print("Start Docker and try again") + raise typer.Exit(1) + + # Check RUNPOD_API_KEY + import os + + if not os.getenv("RUNPOD_API_KEY"): + console.print("[red]Error:[/red] RUNPOD_API_KEY environment variable not set") + console.print("Set it with: export RUNPOD_API_KEY=your-api-key") + raise typer.Exit(1) + + +def _run_flash_build() -> None: + """Run flash build command.""" + console.print("[cyan]Running flash build...[/cyan]") + result = subprocess.run( + ["flash", "build", "--keep-build", "--use-local-flash"], + capture_output=False, + ) + if result.returncode != 0: + console.print("[red]Error:[/red] flash build failed") + raise typer.Exit(1) + + +def _get_manifest_provisioning_code() -> str: + """Generate Python code to provision resources from flash_manifest.json. + + Uses the manifest as a guide to discover which modules contain resource configs. + Imports the actual resource configs from source (endpoint files) to get full + configuration (workers, GPUs, etc.). This ensures test-mothership provisions + exactly what was built, without discovering skeleton templates. + + Returns: + Python code as a string to be executed + """ + return """ +import asyncio +import importlib +import json +import logging +import os +import sys +from pathlib import Path +from runpod_flash.core.deployment import DeploymentOrchestrator + +logger = logging.getLogger(__name__) + +# Configure logging to match the rest of the system +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s | %(levelname)-5s | %(message)s', + datefmt='%Y-%m-%d %H:%M:%S' +) + +async def provision_from_manifest(): + manifest_path = Path("flash_manifest.json") + if not manifest_path.exists(): + print("[dim]No flash_manifest.json found, skipping manifest-based provisioning[/dim]") + return + + try: + with open(manifest_path) as f: + manifest = json.load(f) + except Exception as e: + logger.error(f"Error loading manifest: {e}") + return + + # Set test-mothership mode for resource naming + os.environ["FLASH_IS_TEST_MOTHERSHIP"] = "true" + + resources = [] + for resource_name, resource_data in manifest.get("resources", {}).items(): + try: + # Get list of modules that contain this resource's functions + functions = resource_data.get("functions", []) + if not functions: + logger.warning(f"No functions found for resource {resource_name}") + continue + + # Import the first function's module to get access to the config + first_func = functions[0] + module_name = first_func.get("module") + if not module_name: + logger.warning(f"No module found for resource {resource_name}") + continue + + # Import the module and look for resource config variable + try: + module = importlib.import_module(module_name) + + config = None + + # Try config_variable from manifest first (most reliable) + config_variable = resource_data.get("config_variable") + if config_variable and hasattr(module, config_variable): + config = getattr(module, config_variable) + logger.info(f"Loaded resource config from {module_name}: {config.name} (variable: {config_variable})") + else: + # Fallback to old search logic for backward compatibility + config_names = [ + "gpu_config", "cpu_config", + "resource_config", "config", + f"{resource_name.lower()}_config", + ] + + for config_name in config_names: + if hasattr(module, config_name): + config = getattr(module, config_name) + break + + if config: + logger.info(f"Loaded resource config from {module_name}: {config.name}") + else: + logger.warning(f"No config variable found in {module_name} for {resource_name}") + + if config: + # Apply test-mothership naming convention + if not resource_name.startswith("tmp-"): + config.name = f"tmp-{resource_name}" + else: + config.name = resource_name + + resources.append(config) + + except Exception as e: + logger.warning(f"Failed to import resource config from {module_name}: {e}") + + except Exception as e: + logger.error(f"Failed to process resource {resource_name}: {e}") + + if resources: + try: + logger.info(f"Provisioning {len(resources)} resource(s)...") + orchestrator = DeploymentOrchestrator() + await orchestrator.deploy_all(resources, show_progress=True) + except Exception as e: + logger.warning(f"Provisioning error: {e}") + else: + logger.warning("No resources loaded from manifest") + +asyncio.run(provision_from_manifest()) +""" + + +def _create_entrypoint_script(build_dir: str) -> None: + """Create entrypoint.sh script for Docker container. + + This script handles signal trapping and cleanup on shutdown. + It runs manifest-based provisioning then flash run (without --auto-provision + to avoid duplicate discovery from bundled dependencies). + """ + build_path = Path(build_dir) + + # Ensure build directory exists + if not build_path.exists(): + console.print( + f"[yellow]Warning:[/yellow] Build directory {build_dir} does not exist" + ) + return + + script_path = build_path / "entrypoint.sh" + provisioning_script_path = build_path / "provision_from_manifest.py" + + # Write provisioning script to file + provisioning_code = _get_manifest_provisioning_code() + provisioning_script_path.write_text(provisioning_code) + + script_content = """#!/bin/bash +set -e + +# Ensure bundled dependencies are available to Python +# /workspace contains all the pip-installed packages (.so files, pure Python modules, etc) +export PYTHONPATH="/workspace:${PYTHONPATH}" + +# Signal test-mothership provisioning context for resource naming +export FLASH_IS_TEST_MOTHERSHIP="true" + +cleanup() { + echo "" + echo "==========================================" + echo "Shutting down test-mothership..." + echo "Cleaning up all temporary endpoints..." + echo "==========================================" + python -m runpod_flash.cli.main undeploy --all --force || true + echo "Cleanup complete" + exit 0 +} + +trap cleanup SIGTERM SIGINT + +echo "==========================================" +echo "Starting mothership test environment" +echo "Phase 1: Mothership container startup" +echo "==========================================" + +# Provision resources from manifest before starting server +# This uses the same method as production mothership, avoiding +# false discovery from bundled skeleton templates +python3 provision_from_manifest.py + +# Start server without --auto-provision to avoid re-discovering resources +python -m runpod_flash.cli.main run --host 0.0.0.0 --port 8000 & +PID=$! + +wait $PID +""" + + script_path.write_text(script_content) + script_path.chmod(0o755) + + +def _display_test_objectives() -> None: + """Display what test-mothership tests and important warnings.""" + objectives_text = """[bold cyan]What this tests:[/bold cyan] +• Mothership container deployment +• Child endpoint auto-provisioning via State Manager +• Manifest persistence and State Manager integration + +[bold yellow]⚠ Important:[/bold yellow] +• Uses peer-to-peer architecture (no hub-and-spoke) +• All endpoints query State Manager directly +• Child endpoints are [bold]temporary[/bold] - prefixed with 'tmp-' +• All child endpoints will be [bold]automatically cleaned up[/bold] on shutdown + +[dim]These are test deployments only. Use 'flash deploy' for production.[/dim]""" + + console.print( + Panel( + objectives_text, + title="Test-Mothership Overview", + border_style="cyan", + ) + ) + console.print() + + +def _display_config(build_dir: str, image: str, port: int, endpoint_id: str) -> None: + """Display test configuration.""" + config_text = f"""[bold]Build directory:[/bold] {build_dir} +[bold]Command:[/bold] flash run +[bold]Docker image:[/bold] {image} +[bold]Endpoint ID:[/bold] {endpoint_id} +[bold]Port:[/bold] http://localhost:{port}""" + + console.print(Panel(config_text, title="🚀 Starting mothership test container")) + + +def _build_docker_command( + image: str, port: int, endpoint_id: str, build_dir: str +) -> list: + """Build the docker run command.""" + import os + + build_path = Path(build_dir).resolve() + + cmd = [ + "docker", + "run", + "--platform", + "linux/amd64", + "--rm", + ] + + # Add interactive flags only if running in a TTY environment + if sys.stdin.isatty() and sys.stdout.isatty(): + cmd.extend(["-it"]) + + cmd.extend( + [ + "-e", + "FLASH_IS_MOTHERSHIP=true", + "-e", + "FLASH_IS_TEST_MOTHERSHIP=true", + "-e", + f"RUNPOD_ENDPOINT_ID={endpoint_id}", + "-e", + f"RUNPOD_API_KEY={os.getenv('RUNPOD_API_KEY')}", + "-e", + "FLASH_MANIFEST_PATH=/workspace/flash_manifest.json", + "-v", + f"{build_path}:/workspace", + "-p", + f"{port}:8000", + "--workdir", + "/workspace", + image, + "/workspace/entrypoint.sh", + ] + ) + + return cmd + + +def _run_docker_container(docker_cmd: list, port: int) -> None: + """Run the Docker container with helpful output.""" + console.print("[cyan]✅ Container started successfully[/cyan]\n") + console.print(f"[dim]Local: http://localhost:{port}[/dim]\n") + console.print("[dim]Verification commands:[/dim]") + console.print(f"[dim] Health: curl http://localhost:{port}/ping[/dim]") + console.print( + "[dim] State Manager Query: All endpoints query State Manager directly[/dim]" + ) + console.print("[dim] No /manifest endpoint - peer-to-peer architecture[/dim]\n") + console.print("[bold]Test phases:[/bold]") + console.print(" [dim]1. Mothership startup and health check[/dim]") + console.print( + " [dim]2. Auto-provisioning child endpoints (prefixed with 'tmp-')[/dim]" + ) + console.print(" [dim]3. Manifest update with child endpoint URLs[/dim]") + console.print() + console.print("[dim]Watch container logs below for provisioning progress...[/dim]") + console.print("[dim]Press Ctrl+C to stop and cleanup all endpoints.\n[/dim]") + + try: + result = subprocess.run(docker_cmd, check=False, capture_output=False) + if result.returncode != 0: + console.print( + "\n[yellow]Container exited with an error.[/yellow] " + "Check the logs above for details. Common issues: missing RUNPOD_API_KEY, " + "port already in use, or Docker daemon not running." + ) + except KeyboardInterrupt: + console.print("\n[yellow]Container stopped[/yellow]") + except Exception as e: + console.print(f"[red]Error running container:[/red] {e}") + raise typer.Exit(1) diff --git a/src/tetra_rp/cli/commands/undeploy.py b/src/runpod_flash/cli/commands/undeploy.py similarity index 99% rename from src/tetra_rp/cli/commands/undeploy.py rename to src/runpod_flash/cli/commands/undeploy.py index 3ed3ab49..73974f94 100644 --- a/src/tetra_rp/cli/commands/undeploy.py +++ b/src/runpod_flash/cli/commands/undeploy.py @@ -25,7 +25,7 @@ def _get_resource_manager(): at CLI startup time. This allows fast commands like 'flash init' to run without loading unnecessary dependencies. - Can be mocked in tests: @patch('tetra_rp.cli.commands.undeploy._get_resource_manager') + Can be mocked in tests: @patch('runpod_flash.cli.commands.undeploy._get_resource_manager') """ from ...core.resources.resource_manager import ResourceManager @@ -88,7 +88,7 @@ def _get_resource_type(resource) -> str: def list_command(): - """List all deployed endpoints tracked in .tetra_resources.pkl.""" + """List all deployed endpoints tracked in .runpod/resources.pkl.""" manager = _get_resource_manager() all_resources = manager.list_all_resources() resources = _get_serverless_resources(all_resources) diff --git a/src/tetra_rp/cli/docs/README.md b/src/runpod_flash/cli/docs/README.md similarity index 100% rename from src/tetra_rp/cli/docs/README.md rename to src/runpod_flash/cli/docs/README.md diff --git a/src/tetra_rp/cli/docs/flash-build.md b/src/runpod_flash/cli/docs/flash-build.md similarity index 95% rename from src/tetra_rp/cli/docs/flash-build.md rename to src/runpod_flash/cli/docs/flash-build.md index fef49480..9fa94eb5 100644 --- a/src/tetra_rp/cli/docs/flash-build.md +++ b/src/runpod_flash/cli/docs/flash-build.md @@ -191,7 +191,7 @@ Successful build displays: Ensure your project has `@remote` decorated functions in `workers/` directory: ```python -from tetra_rp import remote, LiveServerless +from runpod_flash import remote, LiveServerless gpu_config = LiveServerless(name="my-gpu") @@ -245,9 +245,9 @@ flash build --exclude torch,torchvision,torchaudio flash build --exclude numpy,scipy,pillow ``` -### Base Image Package Reference (worker-tetra) +### Base Image Package Reference (worker-flash) -Check the [worker-tetra repository](https://github.com/runpod-workers/worker-tetra) for current base images and pre-installed packages. +Check the [worker-flash repository](https://github.com/runpod-workers/worker-flash) for current base images and pre-installed packages. **Base image patterns** (check repository for current versions): @@ -258,7 +258,7 @@ Check the [worker-tetra repository](https://github.com/runpod-workers/worker-tet | `Dockerfile-lb` (GPU LoadBalanced) | `pytorch/pytorch:*-cuda*-cudnn*-runtime` | torch, torchvision, torchaudio | `--exclude torch,torchvision,torchaudio` | | `Dockerfile-lb-cpu` (CPU LoadBalanced) | `python:*-slim` | **None** | Do not exclude ML packages | -**Common pre-installed packages** (versions change - verify in [worker-tetra Dockerfiles](https://github.com/runpod-workers/worker-tetra)): +**Common pre-installed packages** (versions change - verify in [worker-flash Dockerfiles](https://github.com/runpod-workers/worker-flash)): - cloudpickle - pydantic - requests @@ -269,7 +269,7 @@ Check the [worker-tetra repository](https://github.com/runpod-workers/worker-tet **Important:** - Only exclude packages you're certain exist in your base image - Check your resource config's base image before excluding -- Verify current versions in the [worker-tetra repository](https://github.com/runpod-workers/worker-tetra) +- Verify current versions in the [worker-flash repository](https://github.com/runpod-workers/worker-flash) - CPU deployments: Do NOT exclude torch (not pre-installed) - GPU deployments: Safe to exclude torch/torchvision/torchaudio @@ -277,7 +277,7 @@ Check the [worker-tetra repository](https://github.com/runpod-workers/worker-tet 1. Check your `@remote` decorator's `resource_config` 2. GPU configs use PyTorch base → exclude torch packages 3. CPU configs use Python slim → bundle all ML packages -4. When in doubt, check the [worker-tetra Dockerfiles](https://github.com/runpod-workers/worker-tetra) +4. When in doubt, check the [worker-flash Dockerfiles](https://github.com/runpod-workers/worker-flash) ## Next Steps diff --git a/src/tetra_rp/cli/docs/flash-init.md b/src/runpod_flash/cli/docs/flash-init.md similarity index 100% rename from src/tetra_rp/cli/docs/flash-init.md rename to src/runpod_flash/cli/docs/flash-init.md diff --git a/src/tetra_rp/cli/docs/flash-run.md b/src/runpod_flash/cli/docs/flash-run.md similarity index 100% rename from src/tetra_rp/cli/docs/flash-run.md rename to src/runpod_flash/cli/docs/flash-run.md diff --git a/src/tetra_rp/cli/docs/flash-undeploy.md b/src/runpod_flash/cli/docs/flash-undeploy.md similarity index 91% rename from src/tetra_rp/cli/docs/flash-undeploy.md rename to src/runpod_flash/cli/docs/flash-undeploy.md index e69e319d..949c5d15 100644 --- a/src/tetra_rp/cli/docs/flash-undeploy.md +++ b/src/runpod_flash/cli/docs/flash-undeploy.md @@ -1,6 +1,6 @@ # flash undeploy -Manage and delete RunPod serverless endpoints deployed via Tetra. +Manage and delete RunPod serverless endpoints deployed via Flash. ## Synopsis @@ -12,7 +12,7 @@ flash undeploy [NAME|list] [OPTIONS] The `flash undeploy` command manages RunPod serverless endpoints that were deployed using the `@remote` decorator. It provides multiple ways to delete endpoints and clean up tracking state. -When you deploy functions with `@remote`, Tetra tracks them in `.tetra_resources.pkl`. The undeploy command helps you manage these endpoints through deletion and cleanup operations. +When you deploy functions with `@remote`, Flash tracks them in `.runpod/resources.pkl`. The undeploy command helps you manage these endpoints through deletion and cleanup operations. ## Usage Modes @@ -90,7 +90,7 @@ Remove inactive endpoints from tracking without API deletion: flash undeploy --cleanup-stale ``` -**Use case:** When endpoints are deleted via RunPod UI or API (not through Tetra), the tracking file becomes stale. This command identifies and removes those orphaned entries. +**Use case:** When endpoints are deleted via RunPod UI or API (not through Flash), the tracking file becomes stale. This command identifies and removes those orphaned entries. **Behavior:** 1. Checks health status of all tracked endpoints @@ -173,7 +173,7 @@ The Status column performs a health check API call for each endpoint. This: ## Tracking File -Endpoints are tracked in `.tetra_resources.pkl` at your project root. +Endpoints are tracked in `.runpod/resources.pkl`. **Important:** - This file is in `.gitignore` (never commit) @@ -186,16 +186,16 @@ Endpoints are tracked in `.tetra_resources.pkl` at your project root. When you use the `@remote` decorator: ```python -from tetra_rp import remote, LiveServerless +from runpod_flash import remote, LiveServerless @remote(resource_config=LiveServerless(name="my-api")) def my_function(data): return {"result": data} ``` -Tetra automatically: +Flash automatically: 1. Deploys endpoint to RunPod -2. Tracks in `.tetra_resources.pkl` +2. Tracks in `.runpod/resources.pkl` 3. Reuses endpoint on subsequent calls To clean up: @@ -243,4 +243,4 @@ flash undeploy list - [Flash CLI Overview](./README.md) - [RunPod Serverless Documentation](https://docs.runpod.io/serverless/overview) -- [Tetra Documentation](../../../README.md) +- [Flash Documentation](../../../README.md) diff --git a/src/tetra_rp/cli/main.py b/src/runpod_flash/cli/main.py similarity index 98% rename from src/tetra_rp/cli/main.py rename to src/runpod_flash/cli/main.py index f1735aba..04019b2c 100644 --- a/src/tetra_rp/cli/main.py +++ b/src/runpod_flash/cli/main.py @@ -19,7 +19,7 @@ def get_version() -> str: """Get the package version from metadata.""" try: - return metadata.version("tetra_rp") + return metadata.version("runpod-flash") except metadata.PackageNotFoundError: return "unknown" diff --git a/src/tetra_rp/cli/utils/__init__.py b/src/runpod_flash/cli/utils/__init__.py similarity index 100% rename from src/tetra_rp/cli/utils/__init__.py rename to src/runpod_flash/cli/utils/__init__.py diff --git a/src/tetra_rp/cli/utils/app.py b/src/runpod_flash/cli/utils/app.py similarity index 100% rename from src/tetra_rp/cli/utils/app.py rename to src/runpod_flash/cli/utils/app.py diff --git a/src/tetra_rp/cli/utils/conda.py b/src/runpod_flash/cli/utils/conda.py similarity index 100% rename from src/tetra_rp/cli/utils/conda.py rename to src/runpod_flash/cli/utils/conda.py diff --git a/src/tetra_rp/cli/utils/deployment.py b/src/runpod_flash/cli/utils/deployment.py similarity index 97% rename from src/tetra_rp/cli/utils/deployment.py rename to src/runpod_flash/cli/utils/deployment.py index 694241af..3f586b21 100644 --- a/src/tetra_rp/cli/utils/deployment.py +++ b/src/runpod_flash/cli/utils/deployment.py @@ -7,10 +7,10 @@ from datetime import datetime from pathlib import Path -from tetra_rp.config import get_paths -from tetra_rp.core.resources.app import FlashApp -from tetra_rp.core.resources.resource_manager import ResourceManager -from tetra_rp.runtime.mothership_provisioner import create_resource_from_manifest +from runpod_flash.config import get_paths +from runpod_flash.core.resources.app import FlashApp +from runpod_flash.core.resources.resource_manager import ResourceManager +from runpod_flash.runtime.mothership_provisioner import create_resource_from_manifest log = logging.getLogger(__name__) @@ -40,8 +40,8 @@ def save_deployment_environments(environments: Dict[str, Dict[str, Any]]): paths = get_paths() deployments_file = paths.deployments_file - # Ensure .tetra directory exists - paths.ensure_tetra_dir() + # Ensure .flash directory exists + paths.ensure_flash_dir() with open(deployments_file, "w") as f: json.dump(environments, f, indent=2) @@ -425,7 +425,7 @@ async def deploy_to_environment( """Deploy current project to environment. Raises: - tetra_rp.core.resources.app.FlashEnvironmentNotFoundError: If the environment does not exist + runpod_flash.core.resources.app.FlashEnvironmentNotFoundError: If the environment does not exist FileNotFoundError: If manifest not found ValueError: If manifest is invalid """ diff --git a/src/tetra_rp/cli/utils/ignore.py b/src/runpod_flash/cli/utils/ignore.py similarity index 99% rename from src/tetra_rp/cli/utils/ignore.py rename to src/runpod_flash/cli/utils/ignore.py index 68bf483f..bd3b8c7b 100644 --- a/src/tetra_rp/cli/utils/ignore.py +++ b/src/runpod_flash/cli/utils/ignore.py @@ -67,7 +67,7 @@ def load_ignore_patterns(project_dir: Path) -> pathspec.PathSpec: # Always exclude build artifacts and Python bytecode always_ignore = [ ".build/", - ".tetra/", + ".flash/", "*.tar.gz", ".git/", "__pycache__/", diff --git a/src/tetra_rp/cli/utils/skeleton.py b/src/runpod_flash/cli/utils/skeleton.py similarity index 100% rename from src/tetra_rp/cli/utils/skeleton.py rename to src/runpod_flash/cli/utils/skeleton.py diff --git a/src/tetra_rp/cli/utils/skeleton_template/.env.example b/src/runpod_flash/cli/utils/skeleton_template/.env.example similarity index 100% rename from src/tetra_rp/cli/utils/skeleton_template/.env.example rename to src/runpod_flash/cli/utils/skeleton_template/.env.example diff --git a/src/tetra_rp/cli/utils/skeleton_template/.flashignore b/src/runpod_flash/cli/utils/skeleton_template/.flashignore similarity index 94% rename from src/tetra_rp/cli/utils/skeleton_template/.flashignore rename to src/runpod_flash/cli/utils/skeleton_template/.flashignore index ea5988c4..4ce0adca 100644 --- a/src/tetra_rp/cli/utils/skeleton_template/.flashignore +++ b/src/runpod_flash/cli/utils/skeleton_template/.flashignore @@ -27,7 +27,7 @@ build/ *.egg-info/ # Flash resources -.tetra_resources.pkl +.runpod/ # Tests tests/ diff --git a/src/tetra_rp/cli/utils/skeleton_template/.gitignore b/src/runpod_flash/cli/utils/skeleton_template/.gitignore similarity index 93% rename from src/tetra_rp/cli/utils/skeleton_template/.gitignore rename to src/runpod_flash/cli/utils/skeleton_template/.gitignore index 9e84778e..f0673581 100644 --- a/src/tetra_rp/cli/utils/skeleton_template/.gitignore +++ b/src/runpod_flash/cli/utils/skeleton_template/.gitignore @@ -36,7 +36,7 @@ wheels/ .env.local # Flash -.tetra_resources.pkl +.runpod/ dist/ # OS diff --git a/src/tetra_rp/cli/utils/skeleton_template/README.md b/src/runpod_flash/cli/utils/skeleton_template/README.md similarity index 98% rename from src/tetra_rp/cli/utils/skeleton_template/README.md rename to src/runpod_flash/cli/utils/skeleton_template/README.md index 9cc0883d..864b5f21 100644 --- a/src/tetra_rp/cli/utils/skeleton_template/README.md +++ b/src/runpod_flash/cli/utils/skeleton_template/README.md @@ -180,7 +180,7 @@ flash deploy send production 1. Create `workers/my_worker/endpoint.py`: ```python -from tetra_rp import remote, LiveServerless +from runpod_flash import remote, LiveServerless config = LiveServerless(name="my_worker") @@ -213,7 +213,7 @@ app.include_router(my_router, prefix="/my_worker") Same pattern but use `CpuLiveServerless`: ```python -from tetra_rp import remote, CpuLiveServerless, CpuInstanceType +from runpod_flash import remote, CpuLiveServerless, CpuInstanceType config = CpuLiveServerless( name="my_cpu_worker", diff --git a/src/tetra_rp/cli/utils/skeleton_template/main.py b/src/runpod_flash/cli/utils/skeleton_template/main.py similarity index 100% rename from src/tetra_rp/cli/utils/skeleton_template/main.py rename to src/runpod_flash/cli/utils/skeleton_template/main.py diff --git a/src/tetra_rp/cli/utils/skeleton_template/mothership.py b/src/runpod_flash/cli/utils/skeleton_template/mothership.py similarity index 93% rename from src/tetra_rp/cli/utils/skeleton_template/mothership.py rename to src/runpod_flash/cli/utils/skeleton_template/mothership.py index a68303f2..06ccb235 100644 --- a/src/tetra_rp/cli/utils/skeleton_template/mothership.py +++ b/src/runpod_flash/cli/utils/skeleton_template/mothership.py @@ -16,7 +16,7 @@ Documentation: https://docs.runpod.io/flash/mothership """ -from tetra_rp import CpuLiveLoadBalancer +from runpod_flash import CpuLiveLoadBalancer # Mothership endpoint configuration # This serves your FastAPI app routes from main.py @@ -37,7 +37,7 @@ # Use GPU-based load balancer instead of CPU # (requires importing LiveLoadBalancer) -# from tetra_rp import LiveLoadBalancer +# from runpod_flash import LiveLoadBalancer # mothership = LiveLoadBalancer( # name="mothership", # gpus=[GpuGroup.ANY], diff --git a/src/tetra_rp/cli/utils/skeleton_template/pyproject.toml b/src/runpod_flash/cli/utils/skeleton_template/pyproject.toml similarity index 98% rename from src/tetra_rp/cli/utils/skeleton_template/pyproject.toml rename to src/runpod_flash/cli/utils/skeleton_template/pyproject.toml index 70b28a5b..7987ad22 100644 --- a/src/tetra_rp/cli/utils/skeleton_template/pyproject.toml +++ b/src/runpod_flash/cli/utils/skeleton_template/pyproject.toml @@ -9,7 +9,7 @@ description = "Flash serverless application" readme = "README.md" requires-python = ">=3.11" dependencies = [ - "tetra-rp", + "runpod-flash", "fastapi>=0.104.0", "uvicorn>=0.24.0", ] diff --git a/src/runpod_flash/cli/utils/skeleton_template/requirements.txt b/src/runpod_flash/cli/utils/skeleton_template/requirements.txt new file mode 100644 index 00000000..a73ed1ae --- /dev/null +++ b/src/runpod_flash/cli/utils/skeleton_template/requirements.txt @@ -0,0 +1 @@ +runpod-flash diff --git a/src/tetra_rp/cli/utils/skeleton_template/workers/__init__.py b/src/runpod_flash/cli/utils/skeleton_template/workers/__init__.py similarity index 100% rename from src/tetra_rp/cli/utils/skeleton_template/workers/__init__.py rename to src/runpod_flash/cli/utils/skeleton_template/workers/__init__.py diff --git a/src/tetra_rp/cli/utils/skeleton_template/workers/cpu/__init__.py b/src/runpod_flash/cli/utils/skeleton_template/workers/cpu/__init__.py similarity index 100% rename from src/tetra_rp/cli/utils/skeleton_template/workers/cpu/__init__.py rename to src/runpod_flash/cli/utils/skeleton_template/workers/cpu/__init__.py diff --git a/src/tetra_rp/cli/utils/skeleton_template/workers/cpu/endpoint.py b/src/runpod_flash/cli/utils/skeleton_template/workers/cpu/endpoint.py similarity index 94% rename from src/tetra_rp/cli/utils/skeleton_template/workers/cpu/endpoint.py rename to src/runpod_flash/cli/utils/skeleton_template/workers/cpu/endpoint.py index 3de9cbfa..762e8f7d 100644 --- a/src/tetra_rp/cli/utils/skeleton_template/workers/cpu/endpoint.py +++ b/src/runpod_flash/cli/utils/skeleton_template/workers/cpu/endpoint.py @@ -1,4 +1,4 @@ -from tetra_rp import CpuLiveServerless, remote +from runpod_flash import CpuLiveServerless, remote cpu_config = CpuLiveServerless( name="cpu_worker", diff --git a/src/tetra_rp/cli/utils/skeleton_template/workers/gpu/__init__.py b/src/runpod_flash/cli/utils/skeleton_template/workers/gpu/__init__.py similarity index 100% rename from src/tetra_rp/cli/utils/skeleton_template/workers/gpu/__init__.py rename to src/runpod_flash/cli/utils/skeleton_template/workers/gpu/__init__.py diff --git a/src/tetra_rp/cli/utils/skeleton_template/workers/gpu/endpoint.py b/src/runpod_flash/cli/utils/skeleton_template/workers/gpu/endpoint.py similarity index 96% rename from src/tetra_rp/cli/utils/skeleton_template/workers/gpu/endpoint.py rename to src/runpod_flash/cli/utils/skeleton_template/workers/gpu/endpoint.py index 905fb833..1d125c19 100644 --- a/src/tetra_rp/cli/utils/skeleton_template/workers/gpu/endpoint.py +++ b/src/runpod_flash/cli/utils/skeleton_template/workers/gpu/endpoint.py @@ -1,4 +1,4 @@ -from tetra_rp import GpuGroup, LiveServerless, remote +from runpod_flash import GpuGroup, LiveServerless, remote gpu_config = LiveServerless( name="gpu_worker", diff --git a/src/tetra_rp/client.py b/src/runpod_flash/client.py similarity index 100% rename from src/tetra_rp/client.py rename to src/runpod_flash/client.py diff --git a/src/runpod_flash/config.py b/src/runpod_flash/config.py new file mode 100644 index 00000000..d83bc7ca --- /dev/null +++ b/src/runpod_flash/config.py @@ -0,0 +1,29 @@ +"""Configuration management for runpod-flash CLI.""" + +from pathlib import Path +from typing import NamedTuple + + +class FlashPaths(NamedTuple): + """Paths for runpod-flash configuration and data.""" + + flash_dir: Path + config_file: Path + deployments_file: Path + + def ensure_flash_dir(self) -> None: + """Ensure the .flash directory exists.""" + self.flash_dir.mkdir(exist_ok=True) + + +def get_paths() -> FlashPaths: + """Get standardized paths for runpod-flash configuration.""" + flash_dir = Path.cwd() / ".flash" + config_file = flash_dir / "config.json" + deployments_file = flash_dir / "deployments.json" + + return FlashPaths( + flash_dir=flash_dir, + config_file=config_file, + deployments_file=deployments_file, + ) diff --git a/src/tetra_rp/core/__init__.py b/src/runpod_flash/core/__init__.py similarity index 100% rename from src/tetra_rp/core/__init__.py rename to src/runpod_flash/core/__init__.py diff --git a/src/tetra_rp/core/api/__init__.py b/src/runpod_flash/core/api/__init__.py similarity index 100% rename from src/tetra_rp/core/api/__init__.py rename to src/runpod_flash/core/api/__init__.py diff --git a/src/tetra_rp/core/api/runpod.py b/src/runpod_flash/core/api/runpod.py similarity index 99% rename from src/tetra_rp/core/api/runpod.py rename to src/runpod_flash/core/api/runpod.py index 9d02175a..a54e2f8f 100644 --- a/src/tetra_rp/core/api/runpod.py +++ b/src/runpod_flash/core/api/runpod.py @@ -11,8 +11,8 @@ import aiohttp from aiohttp.resolver import ThreadedResolver -from tetra_rp.core.exceptions import RunpodAPIKeyError -from tetra_rp.runtime.exceptions import GraphQLMutationError, GraphQLQueryError +from runpod_flash.core.exceptions import RunpodAPIKeyError +from runpod_flash.runtime.exceptions import GraphQLMutationError, GraphQLQueryError log = logging.getLogger(__name__) diff --git a/src/tetra_rp/core/deployment.py b/src/runpod_flash/core/deployment.py similarity index 100% rename from src/tetra_rp/core/deployment.py rename to src/runpod_flash/core/deployment.py diff --git a/src/tetra_rp/core/discovery.py b/src/runpod_flash/core/discovery.py similarity index 100% rename from src/tetra_rp/core/discovery.py rename to src/runpod_flash/core/discovery.py diff --git a/src/tetra_rp/core/exceptions.py b/src/runpod_flash/core/exceptions.py similarity index 97% rename from src/tetra_rp/core/exceptions.py rename to src/runpod_flash/core/exceptions.py index 4df84558..37ca57b0 100644 --- a/src/tetra_rp/core/exceptions.py +++ b/src/runpod_flash/core/exceptions.py @@ -1,4 +1,4 @@ -"""Custom exceptions for tetra_rp. +"""Custom exceptions for runpod_flash. Provides clear, actionable error messages for common failure scenarios. """ diff --git a/src/tetra_rp/core/resources/__init__.py b/src/runpod_flash/core/resources/__init__.py similarity index 100% rename from src/tetra_rp/core/resources/__init__.py rename to src/runpod_flash/core/resources/__init__.py diff --git a/src/tetra_rp/core/resources/app.py b/src/runpod_flash/core/resources/app.py similarity index 99% rename from src/tetra_rp/core/resources/app.py rename to src/runpod_flash/core/resources/app.py index 6567cd0b..9c17828b 100644 --- a/src/tetra_rp/core/resources/app.py +++ b/src/runpod_flash/core/resources/app.py @@ -160,7 +160,7 @@ def __init__(self, name: str, id: Optional[str] = "", eager_hydrate: bool = Fals asyncio.run(self._hydrate()) def remote(self, *args, **kwargs): - from tetra_rp.client import remote as remote_decorator + from runpod_flash.client import remote as remote_decorator resource_config = kwargs.get("resource_config") diff --git a/src/tetra_rp/core/resources/base.py b/src/runpod_flash/core/resources/base.py similarity index 100% rename from src/tetra_rp/core/resources/base.py rename to src/runpod_flash/core/resources/base.py diff --git a/src/tetra_rp/core/resources/cloud.py b/src/runpod_flash/core/resources/cloud.py similarity index 100% rename from src/tetra_rp/core/resources/cloud.py rename to src/runpod_flash/core/resources/cloud.py diff --git a/src/tetra_rp/core/resources/constants.py b/src/runpod_flash/core/resources/constants.py similarity index 66% rename from src/tetra_rp/core/resources/constants.py rename to src/runpod_flash/core/resources/constants.py index 5c0c5b33..1e0bdc12 100644 --- a/src/tetra_rp/core/resources/constants.py +++ b/src/runpod_flash/core/resources/constants.py @@ -20,16 +20,14 @@ def _endpoint_domain_from_base_url(base_url: str) -> str: # Docker image configuration -TETRA_IMAGE_TAG = os.environ.get("TETRA_IMAGE_TAG", "latest") -_RESOLVED_TAG = TETRA_IMAGE_TAG - -TETRA_GPU_IMAGE = os.environ.get("TETRA_GPU_IMAGE", f"runpod/tetra-rp:{_RESOLVED_TAG}") -TETRA_CPU_IMAGE = os.environ.get( - "TETRA_CPU_IMAGE", f"runpod/tetra-rp-cpu:{_RESOLVED_TAG}" -) -TETRA_LB_IMAGE = os.environ.get("TETRA_LB_IMAGE", f"runpod/tetra-rp-lb:{_RESOLVED_TAG}") -TETRA_CPU_LB_IMAGE = os.environ.get( - "TETRA_CPU_LB_IMAGE", f"runpod/tetra-rp-lb-cpu:{_RESOLVED_TAG}" +FLASH_IMAGE_TAG = os.environ.get("FLASH_IMAGE_TAG", "latest") +_RESOLVED_TAG = FLASH_IMAGE_TAG + +FLASH_GPU_IMAGE = os.environ.get("FLASH_GPU_IMAGE", f"runpod/flash:{_RESOLVED_TAG}") +FLASH_CPU_IMAGE = os.environ.get("FLASH_CPU_IMAGE", f"runpod/flash-cpu:{_RESOLVED_TAG}") +FLASH_LB_IMAGE = os.environ.get("FLASH_LB_IMAGE", f"runpod/flash-lb:{_RESOLVED_TAG}") +FLASH_CPU_LB_IMAGE = os.environ.get( + "FLASH_CPU_LB_IMAGE", f"runpod/flash-lb-cpu:{_RESOLVED_TAG}" ) # Worker configuration defaults diff --git a/src/tetra_rp/core/resources/cpu.py b/src/runpod_flash/core/resources/cpu.py similarity index 100% rename from src/tetra_rp/core/resources/cpu.py rename to src/runpod_flash/core/resources/cpu.py diff --git a/src/tetra_rp/core/resources/environment.py b/src/runpod_flash/core/resources/environment.py similarity index 100% rename from src/tetra_rp/core/resources/environment.py rename to src/runpod_flash/core/resources/environment.py diff --git a/src/tetra_rp/core/resources/gpu.py b/src/runpod_flash/core/resources/gpu.py similarity index 100% rename from src/tetra_rp/core/resources/gpu.py rename to src/runpod_flash/core/resources/gpu.py diff --git a/src/tetra_rp/core/resources/live_serverless.py b/src/runpod_flash/core/resources/live_serverless.py similarity index 88% rename from src/tetra_rp/core/resources/live_serverless.py rename to src/runpod_flash/core/resources/live_serverless.py index 5f857953..8ae0b3a5 100644 --- a/src/tetra_rp/core/resources/live_serverless.py +++ b/src/runpod_flash/core/resources/live_serverless.py @@ -2,10 +2,10 @@ from pydantic import model_validator from .constants import ( - TETRA_CPU_IMAGE, - TETRA_CPU_LB_IMAGE, - TETRA_GPU_IMAGE, - TETRA_LB_IMAGE, + FLASH_CPU_IMAGE, + FLASH_CPU_LB_IMAGE, + FLASH_GPU_IMAGE, + FLASH_LB_IMAGE, ) from .load_balancer_sls_resource import ( CpuLoadBalancerSlsResource, @@ -39,13 +39,13 @@ class LiveServerless(LiveServerlessMixin, ServerlessEndpoint): @property def _live_image(self) -> str: - return TETRA_GPU_IMAGE + return FLASH_GPU_IMAGE @model_validator(mode="before") @classmethod def set_live_serverless_template(cls, data: dict): """Set default GPU image for Live Serverless.""" - data["imageName"] = TETRA_GPU_IMAGE + data["imageName"] = FLASH_GPU_IMAGE return data @@ -54,13 +54,13 @@ class CpuLiveServerless(LiveServerlessMixin, CpuServerlessEndpoint): @property def _live_image(self) -> str: - return TETRA_CPU_IMAGE + return FLASH_CPU_IMAGE @model_validator(mode="before") @classmethod def set_live_serverless_template(cls, data: dict): """Set default CPU image for Live Serverless.""" - data["imageName"] = TETRA_CPU_IMAGE + data["imageName"] = FLASH_CPU_IMAGE return data @@ -72,13 +72,13 @@ class LiveLoadBalancer(LiveServerlessMixin, LoadBalancerSlsResource): before deploying to production. Features: - - Locks to Tetra LB image (tetra-rp-lb) + - Locks to Flash LB image (flash-lb) - Direct HTTP execution (not queue-based) - Local development with flash run - Same @remote decorator pattern as LoadBalancerSlsResource Usage: - from tetra_rp import LiveLoadBalancer, remote + from runpod_flash import LiveLoadBalancer, remote api = LiveLoadBalancer(name="api-service") @@ -104,13 +104,13 @@ async def process_data(x: int, y: int): @property def _live_image(self) -> str: - return TETRA_LB_IMAGE + return FLASH_LB_IMAGE @model_validator(mode="before") @classmethod def set_live_lb_template(cls, data: dict): """Set default image for Live Load-Balanced endpoint.""" - data["imageName"] = TETRA_LB_IMAGE + data["imageName"] = FLASH_LB_IMAGE return data @@ -121,14 +121,14 @@ class CpuLiveLoadBalancer(LiveServerlessMixin, CpuLoadBalancerSlsResource): automatic disk sizing and validation. Features: - - Locks to CPU Tetra LB image (tetra-rp-lb-cpu) + - Locks to CPU Flash LB image (flash-lb-cpu) - CPU instance support with automatic disk sizing - Direct HTTP execution (not queue-based) - Local development with flash run - Same @remote decorator pattern as CpuLoadBalancerSlsResource Usage: - from tetra_rp import CpuLiveLoadBalancer, remote + from runpod_flash import CpuLiveLoadBalancer, remote api = CpuLiveLoadBalancer(name="api-service") @@ -149,11 +149,11 @@ async def process_data(x: int, y: int): @property def _live_image(self) -> str: - return TETRA_CPU_LB_IMAGE + return FLASH_CPU_LB_IMAGE @model_validator(mode="before") @classmethod def set_live_cpu_lb_template(cls, data: dict): """Set default CPU image for Live Load-Balanced endpoint.""" - data["imageName"] = TETRA_CPU_LB_IMAGE + data["imageName"] = FLASH_CPU_LB_IMAGE return data diff --git a/src/tetra_rp/core/resources/load_balancer_sls_resource.py b/src/runpod_flash/core/resources/load_balancer_sls_resource.py similarity index 99% rename from src/tetra_rp/core/resources/load_balancer_sls_resource.py rename to src/runpod_flash/core/resources/load_balancer_sls_resource.py index bfe5fd99..7a5042c5 100644 --- a/src/tetra_rp/core/resources/load_balancer_sls_resource.py +++ b/src/runpod_flash/core/resources/load_balancer_sls_resource.py @@ -19,7 +19,7 @@ from pydantic import model_validator -from tetra_rp.core.utils.http import get_authenticated_httpx_client +from runpod_flash.core.utils.http import get_authenticated_httpx_client from .constants import ENDPOINT_DOMAIN from .cpu import CpuInstanceType from .serverless import ServerlessResource, ServerlessType, ServerlessScalerType diff --git a/src/tetra_rp/core/resources/network_volume.py b/src/runpod_flash/core/resources/network_volume.py similarity index 100% rename from src/tetra_rp/core/resources/network_volume.py rename to src/runpod_flash/core/resources/network_volume.py diff --git a/src/tetra_rp/core/resources/resource_manager.py b/src/runpod_flash/core/resources/resource_manager.py similarity index 100% rename from src/tetra_rp/core/resources/resource_manager.py rename to src/runpod_flash/core/resources/resource_manager.py diff --git a/src/tetra_rp/core/resources/serverless.py b/src/runpod_flash/core/resources/serverless.py similarity index 100% rename from src/tetra_rp/core/resources/serverless.py rename to src/runpod_flash/core/resources/serverless.py diff --git a/src/tetra_rp/core/resources/serverless_cpu.py b/src/runpod_flash/core/resources/serverless_cpu.py similarity index 100% rename from src/tetra_rp/core/resources/serverless_cpu.py rename to src/runpod_flash/core/resources/serverless_cpu.py diff --git a/src/tetra_rp/core/resources/template.py b/src/runpod_flash/core/resources/template.py similarity index 100% rename from src/tetra_rp/core/resources/template.py rename to src/runpod_flash/core/resources/template.py diff --git a/src/tetra_rp/core/utils/__init__.py b/src/runpod_flash/core/utils/__init__.py similarity index 100% rename from src/tetra_rp/core/utils/__init__.py rename to src/runpod_flash/core/utils/__init__.py diff --git a/src/tetra_rp/core/utils/backoff.py b/src/runpod_flash/core/utils/backoff.py similarity index 100% rename from src/tetra_rp/core/utils/backoff.py rename to src/runpod_flash/core/utils/backoff.py diff --git a/src/tetra_rp/core/utils/constants.py b/src/runpod_flash/core/utils/constants.py similarity index 78% rename from src/tetra_rp/core/utils/constants.py rename to src/runpod_flash/core/utils/constants.py index bd27e89f..df71be2b 100644 --- a/src/tetra_rp/core/utils/constants.py +++ b/src/runpod_flash/core/utils/constants.py @@ -1,7 +1,7 @@ """ Constants for utility modules and caching configurations. -This module contains configurable constants used across the tetra-rp codebase +This module contains configurable constants used across the runpod-flash codebase to ensure consistency and easy maintenance. """ diff --git a/src/tetra_rp/core/utils/file_lock.py b/src/runpod_flash/core/utils/file_lock.py similarity index 100% rename from src/tetra_rp/core/utils/file_lock.py rename to src/runpod_flash/core/utils/file_lock.py diff --git a/src/tetra_rp/core/utils/http.py b/src/runpod_flash/core/utils/http.py similarity index 100% rename from src/tetra_rp/core/utils/http.py rename to src/runpod_flash/core/utils/http.py diff --git a/src/tetra_rp/core/utils/lru_cache.py b/src/runpod_flash/core/utils/lru_cache.py similarity index 100% rename from src/tetra_rp/core/utils/lru_cache.py rename to src/runpod_flash/core/utils/lru_cache.py diff --git a/src/tetra_rp/core/utils/singleton.py b/src/runpod_flash/core/utils/singleton.py similarity index 100% rename from src/tetra_rp/core/utils/singleton.py rename to src/runpod_flash/core/utils/singleton.py diff --git a/src/tetra_rp/core/validation.py b/src/runpod_flash/core/validation.py similarity index 90% rename from src/tetra_rp/core/validation.py rename to src/runpod_flash/core/validation.py index 8c398f12..6077a05d 100644 --- a/src/tetra_rp/core/validation.py +++ b/src/runpod_flash/core/validation.py @@ -1,11 +1,11 @@ -"""Validation utilities for tetra_rp configuration. +"""Validation utilities for runpod_flash configuration. Provides validation functions for required environment variables and configuration. """ import os -from tetra_rp.core.exceptions import RunpodAPIKeyError +from runpod_flash.core.exceptions import RunpodAPIKeyError def validate_api_key() -> str: diff --git a/src/tetra_rp/execute_class.py b/src/runpod_flash/execute_class.py similarity index 100% rename from src/tetra_rp/execute_class.py rename to src/runpod_flash/execute_class.py diff --git a/src/tetra_rp/logger.py b/src/runpod_flash/logger.py similarity index 100% rename from src/tetra_rp/logger.py rename to src/runpod_flash/logger.py diff --git a/src/tetra_rp/protos/__init__.py b/src/runpod_flash/protos/__init__.py similarity index 100% rename from src/tetra_rp/protos/__init__.py rename to src/runpod_flash/protos/__init__.py diff --git a/src/tetra_rp/protos/remote_execution.proto b/src/runpod_flash/protos/remote_execution.proto similarity index 99% rename from src/tetra_rp/protos/remote_execution.proto rename to src/runpod_flash/protos/remote_execution.proto index eff88046..59a4e607 100644 --- a/src/tetra_rp/protos/remote_execution.proto +++ b/src/runpod_flash/protos/remote_execution.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package tetra; +package flash; // The remote execution service definition service RemoteExecutor { diff --git a/src/tetra_rp/protos/remote_execution.py b/src/runpod_flash/protos/remote_execution.py similarity index 100% rename from src/tetra_rp/protos/remote_execution.py rename to src/runpod_flash/protos/remote_execution.py diff --git a/src/runpod_flash/protos/remote_execution_pb2.py b/src/runpod_flash/protos/remote_execution_pb2.py new file mode 100644 index 00000000..67c1fd2e --- /dev/null +++ b/src/runpod_flash/protos/remote_execution_pb2.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: remote_execution.proto +# Protobuf Python Version: 5.29.3 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 5, 29, 3, "", "remote_execution.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x16remote_execution.proto\x12\x05\x66lash"\xb0\x05\n\x0f\x46unctionRequest\x12\x1a\n\rfunction_name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rfunction_code\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x0c\n\x04\x61rgs\x18\x03 \x03(\t\x12\x32\n\x06kwargs\x18\x04 \x03(\x0b\x32".flash.FunctionRequest.KwargsEntry\x12\x14\n\x0c\x64\x65pendencies\x18\x05 \x03(\t\x12\x1b\n\x13system_dependencies\x18\x06 \x03(\t\x12\x16\n\x0e\x65xecution_type\x18\x07 \x01(\t\x12\x17\n\nclass_name\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nclass_code\x18\t \x01(\tH\x03\x88\x01\x01\x12\x18\n\x10\x63onstructor_args\x18\n \x03(\t\x12I\n\x12\x63onstructor_kwargs\x18\x0b \x03(\x0b\x32-.flash.FunctionRequest.ConstructorKwargsEntry\x12\x13\n\x0bmethod_name\x18\x0c \x01(\t\x12\x18\n\x0binstance_id\x18\r \x01(\tH\x04\x88\x01\x01\x12\x1b\n\x13\x63reate_new_instance\x18\x0e \x01(\x08\x12!\n\x14\x61\x63\x63\x65lerate_downloads\x18\x13 \x01(\x08H\x05\x88\x01\x01\x1a-\n\x0bKwargsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x38\n\x16\x43onstructorKwargsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x10\n\x0e_function_nameB\x10\n\x0e_function_codeB\r\n\x0b_class_nameB\r\n\x0b_class_codeB\x0e\n\x0c_instance_idB\x17\n\x15_accelerate_downloads"\xa2\x02\n\x10\x46unctionResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x13\n\x06result\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x12\n\x05\x65rror\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06stdout\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0binstance_id\x18\x05 \x01(\tH\x03\x88\x01\x01\x12@\n\rinstance_info\x18\x06 \x03(\x0b\x32).flash.FunctionResponse.InstanceInfoEntry\x1a\x33\n\x11InstanceInfoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\t\n\x07_resultB\x08\n\x06_errorB\t\n\x07_stdoutB\x0e\n\x0c_instance_id2V\n\x0eRemoteExecutor\x12\x44\n\x0f\x45xecuteFunction\x12\x16.flash.FunctionRequest\x1a\x17.flash.FunctionResponse"\x00\x62\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "remote_execution_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals["_FUNCTIONREQUEST_KWARGSENTRY"]._loaded_options = None + _globals["_FUNCTIONREQUEST_KWARGSENTRY"]._serialized_options = b"8\001" + _globals["_FUNCTIONREQUEST_CONSTRUCTORKWARGSENTRY"]._loaded_options = None + _globals["_FUNCTIONREQUEST_CONSTRUCTORKWARGSENTRY"]._serialized_options = b"8\001" + _globals["_FUNCTIONRESPONSE_INSTANCEINFOENTRY"]._loaded_options = None + _globals["_FUNCTIONRESPONSE_INSTANCEINFOENTRY"]._serialized_options = b"8\001" + _globals["_FUNCTIONREQUEST"]._serialized_start = 34 + _globals["_FUNCTIONREQUEST"]._serialized_end = 722 + _globals["_FUNCTIONREQUEST_KWARGSENTRY"]._serialized_start = 512 + _globals["_FUNCTIONREQUEST_KWARGSENTRY"]._serialized_end = 557 + _globals["_FUNCTIONREQUEST_CONSTRUCTORKWARGSENTRY"]._serialized_start = 559 + _globals["_FUNCTIONREQUEST_CONSTRUCTORKWARGSENTRY"]._serialized_end = 615 + _globals["_FUNCTIONRESPONSE"]._serialized_start = 725 + _globals["_FUNCTIONRESPONSE"]._serialized_end = 1015 + _globals["_FUNCTIONRESPONSE_INSTANCEINFOENTRY"]._serialized_start = 916 + _globals["_FUNCTIONRESPONSE_INSTANCEINFOENTRY"]._serialized_end = 967 + _globals["_REMOTEEXECUTOR"]._serialized_start = 1017 + _globals["_REMOTEEXECUTOR"]._serialized_end = 1103 +# @@protoc_insertion_point(module_scope) diff --git a/src/tetra_rp/runtime/__init__.py b/src/runpod_flash/runtime/__init__.py similarity index 100% rename from src/tetra_rp/runtime/__init__.py rename to src/runpod_flash/runtime/__init__.py diff --git a/src/tetra_rp/runtime/circuit_breaker.py b/src/runpod_flash/runtime/circuit_breaker.py similarity index 100% rename from src/tetra_rp/runtime/circuit_breaker.py rename to src/runpod_flash/runtime/circuit_breaker.py diff --git a/src/tetra_rp/runtime/config.py b/src/runpod_flash/runtime/config.py similarity index 100% rename from src/tetra_rp/runtime/config.py rename to src/runpod_flash/runtime/config.py diff --git a/src/tetra_rp/runtime/exceptions.py b/src/runpod_flash/runtime/exceptions.py similarity index 100% rename from src/tetra_rp/runtime/exceptions.py rename to src/runpod_flash/runtime/exceptions.py diff --git a/src/tetra_rp/runtime/generic_handler.py b/src/runpod_flash/runtime/generic_handler.py similarity index 98% rename from src/tetra_rp/runtime/generic_handler.py rename to src/runpod_flash/runtime/generic_handler.py index 0bf9aa39..aba9a645 100644 --- a/src/tetra_rp/runtime/generic_handler.py +++ b/src/runpod_flash/runtime/generic_handler.py @@ -139,7 +139,7 @@ def create_handler(function_registry: Dict[str, Callable]) -> Callable: Example: ```python - from tetra_rp.runtime.generic_handler import create_handler + from runpod_flash.runtime.generic_handler import create_handler from workers.gpu import process_data, analyze_data registry = { diff --git a/src/tetra_rp/runtime/lb_handler.py b/src/runpod_flash/runtime/lb_handler.py similarity index 98% rename from src/tetra_rp/runtime/lb_handler.py rename to src/runpod_flash/runtime/lb_handler.py index 7a317cd5..507fdd73 100644 --- a/src/tetra_rp/runtime/lb_handler.py +++ b/src/runpod_flash/runtime/lb_handler.py @@ -59,7 +59,7 @@ async def execute_remote_function(request: Request) -> Dict[str, Any]: """Framework endpoint for @remote decorator execution. WARNING: This endpoint is INTERNAL to the Flash framework. It should only be - called by the @remote stub from tetra_rp.stubs.load_balancer_sls. Exposing + called by the @remote stub from runpod_flash.stubs.load_balancer_sls. Exposing this endpoint to untrusted clients could allow arbitrary code execution. Accepts serialized function code and arguments, executes them, diff --git a/src/tetra_rp/runtime/load_balancer.py b/src/runpod_flash/runtime/load_balancer.py similarity index 95% rename from src/tetra_rp/runtime/load_balancer.py rename to src/runpod_flash/runtime/load_balancer.py index 3e7272ad..6c32b465 100644 --- a/src/tetra_rp/runtime/load_balancer.py +++ b/src/runpod_flash/runtime/load_balancer.py @@ -5,10 +5,10 @@ import random from typing import TYPE_CHECKING, List, Optional -from tetra_rp.runtime.reliability_config import LoadBalancerStrategy +from runpod_flash.runtime.reliability_config import LoadBalancerStrategy if TYPE_CHECKING: - from tetra_rp.runtime.circuit_breaker import CircuitBreakerRegistry + from runpod_flash.runtime.circuit_breaker import CircuitBreakerRegistry logger = logging.getLogger(__name__) @@ -49,7 +49,7 @@ async def select_endpoint( # Filter out unhealthy endpoints if circuit breaker available healthy_endpoints = endpoints if circuit_breaker_registry is not None: - from tetra_rp.runtime.circuit_breaker import CircuitState + from runpod_flash.runtime.circuit_breaker import CircuitState healthy_endpoints = [ url diff --git a/src/tetra_rp/runtime/manifest_fetcher.py b/src/runpod_flash/runtime/manifest_fetcher.py similarity index 100% rename from src/tetra_rp/runtime/manifest_fetcher.py rename to src/runpod_flash/runtime/manifest_fetcher.py diff --git a/src/tetra_rp/runtime/metrics.py b/src/runpod_flash/runtime/metrics.py similarity index 98% rename from src/tetra_rp/runtime/metrics.py rename to src/runpod_flash/runtime/metrics.py index 3a530cde..7095d9e8 100644 --- a/src/tetra_rp/runtime/metrics.py +++ b/src/runpod_flash/runtime/metrics.py @@ -37,7 +37,7 @@ def to_dict(self) -> Dict[str, Any]: class MetricsCollector: """Collect metrics via structured logging.""" - def __init__(self, namespace: str = "tetra.metrics", enabled: bool = True): + def __init__(self, namespace: str = "flash.metrics", enabled: bool = True): """Initialize metrics collector. Args: @@ -127,7 +127,7 @@ def _emit(self, metric: Metric) -> None: def get_metrics_collector( - namespace: str = "tetra.metrics", enabled: bool = True + namespace: str = "flash.metrics", enabled: bool = True ) -> MetricsCollector: """Get global metrics collector (lazy-loaded). diff --git a/src/tetra_rp/runtime/models.py b/src/runpod_flash/runtime/models.py similarity index 100% rename from src/tetra_rp/runtime/models.py rename to src/runpod_flash/runtime/models.py diff --git a/src/tetra_rp/runtime/mothership_provisioner.py b/src/runpod_flash/runtime/mothership_provisioner.py similarity index 97% rename from src/tetra_rp/runtime/mothership_provisioner.py rename to src/runpod_flash/runtime/mothership_provisioner.py index f6577ec7..cbbd2cf8 100644 --- a/src/tetra_rp/runtime/mothership_provisioner.py +++ b/src/runpod_flash/runtime/mothership_provisioner.py @@ -8,9 +8,9 @@ from pathlib import Path from typing import Any, Dict, List, Optional -from tetra_rp.core.resources.base import DeployableResource -from tetra_rp.core.resources.constants import ENDPOINT_DOMAIN -from tetra_rp.core.resources.resource_manager import ResourceManager +from runpod_flash.core.resources.base import DeployableResource +from runpod_flash.core.resources.constants import ENDPOINT_DOMAIN +from runpod_flash.core.resources.resource_manager import ResourceManager from .state_manager_client import StateManagerClient @@ -240,16 +240,16 @@ def create_resource_from_manifest( Raises: ValueError: If resource type not supported """ - from tetra_rp.core.resources.live_serverless import ( + from runpod_flash.core.resources.live_serverless import ( CpuLiveLoadBalancer, CpuLiveServerless, LiveLoadBalancer, LiveServerless, ) - from tetra_rp.core.resources.load_balancer_sls_resource import ( + from runpod_flash.core.resources.load_balancer_sls_resource import ( LoadBalancerSlsResource, ) - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.core.resources.serverless import ServerlessResource resource_type = resource_data.get("resource_type", "ServerlessResource") diff --git a/src/tetra_rp/runtime/production_wrapper.py b/src/runpod_flash/runtime/production_wrapper.py similarity index 99% rename from src/tetra_rp/runtime/production_wrapper.py rename to src/runpod_flash/runtime/production_wrapper.py index eb600ced..1f613e8c 100644 --- a/src/tetra_rp/runtime/production_wrapper.py +++ b/src/runpod_flash/runtime/production_wrapper.py @@ -3,7 +3,7 @@ import logging from typing import Any, Callable, Dict, Optional -from tetra_rp.core.resources.serverless import ServerlessResource +from runpod_flash.core.resources.serverless import ServerlessResource from .exceptions import RemoteExecutionError from .serialization import serialize_args, serialize_kwargs diff --git a/src/tetra_rp/runtime/reliability_config.py b/src/runpod_flash/runtime/reliability_config.py similarity index 72% rename from src/tetra_rp/runtime/reliability_config.py rename to src/runpod_flash/runtime/reliability_config.py index 46bc6429..ecae6440 100644 --- a/src/tetra_rp/runtime/reliability_config.py +++ b/src/runpod_flash/runtime/reliability_config.py @@ -55,7 +55,7 @@ class MetricsConfig: """Configuration for metrics collection.""" enabled: bool = True - namespace: str = "tetra.metrics" + namespace: str = "flash.metrics" @dataclass @@ -72,47 +72,47 @@ def from_env(cls) -> "ReliabilityConfig": """Load configuration from environment variables. Environment variables: - - TETRA_CIRCUIT_BREAKER_ENABLED: Enable circuit breaker (default: true) - - TETRA_CB_FAILURE_THRESHOLD: Failures before opening (default: 5) - - TETRA_CB_SUCCESS_THRESHOLD: Successes to close (default: 2) - - TETRA_CB_TIMEOUT_SECONDS: Time before half-open (default: 60) - - TETRA_LOAD_BALANCER_ENABLED: Enable load balancer (default: false) - - TETRA_LB_STRATEGY: Load balancer strategy (default: round_robin) - - TETRA_RETRY_ENABLED: Enable retry (default: true) - - TETRA_RETRY_MAX_ATTEMPTS: Max retry attempts (default: 3) - - TETRA_RETRY_BASE_DELAY: Base delay for backoff (default: 0.5) - - TETRA_METRICS_ENABLED: Enable metrics (default: true) + - FLASH_CIRCUIT_BREAKER_ENABLED: Enable circuit breaker (default: true) + - FLASH_CB_FAILURE_THRESHOLD: Failures before opening (default: 5) + - FLASH_CB_SUCCESS_THRESHOLD: Successes to close (default: 2) + - FLASH_CB_TIMEOUT_SECONDS: Time before half-open (default: 60) + - FLASH_LOAD_BALANCER_ENABLED: Enable load balancer (default: false) + - FLASH_LB_STRATEGY: Load balancer strategy (default: round_robin) + - FLASH_RETRY_ENABLED: Enable retry (default: true) + - FLASH_RETRY_MAX_ATTEMPTS: Max retry attempts (default: 3) + - FLASH_RETRY_BASE_DELAY: Base delay for backoff (default: 0.5) + - FLASH_METRICS_ENABLED: Enable metrics (default: true) Returns: ReliabilityConfig initialized from environment variables. """ circuit_breaker = CircuitBreakerConfig( - enabled=os.getenv("TETRA_CIRCUIT_BREAKER_ENABLED", "true").lower() + enabled=os.getenv("FLASH_CIRCUIT_BREAKER_ENABLED", "true").lower() == "true", - failure_threshold=int(os.getenv("TETRA_CB_FAILURE_THRESHOLD", "5")), - success_threshold=int(os.getenv("TETRA_CB_SUCCESS_THRESHOLD", "2")), - timeout_seconds=int(os.getenv("TETRA_CB_TIMEOUT_SECONDS", "60")), + failure_threshold=int(os.getenv("FLASH_CB_FAILURE_THRESHOLD", "5")), + success_threshold=int(os.getenv("FLASH_CB_SUCCESS_THRESHOLD", "2")), + timeout_seconds=int(os.getenv("FLASH_CB_TIMEOUT_SECONDS", "60")), ) - strategy_str = os.getenv("TETRA_LB_STRATEGY", "round_robin").lower() + strategy_str = os.getenv("FLASH_LB_STRATEGY", "round_robin").lower() try: strategy = LoadBalancerStrategy(strategy_str) except ValueError: strategy = LoadBalancerStrategy.ROUND_ROBIN load_balancer = LoadBalancerConfig( - enabled=os.getenv("TETRA_LOAD_BALANCER_ENABLED", "false").lower() == "true", + enabled=os.getenv("FLASH_LOAD_BALANCER_ENABLED", "false").lower() == "true", strategy=strategy, ) retry = RetryConfig( - enabled=os.getenv("TETRA_RETRY_ENABLED", "true").lower() == "true", - max_attempts=int(os.getenv("TETRA_RETRY_MAX_ATTEMPTS", "3")), - base_delay=float(os.getenv("TETRA_RETRY_BASE_DELAY", "0.5")), + enabled=os.getenv("FLASH_RETRY_ENABLED", "true").lower() == "true", + max_attempts=int(os.getenv("FLASH_RETRY_MAX_ATTEMPTS", "3")), + base_delay=float(os.getenv("FLASH_RETRY_BASE_DELAY", "0.5")), ) metrics = MetricsConfig( - enabled=os.getenv("TETRA_METRICS_ENABLED", "true").lower() == "true", + enabled=os.getenv("FLASH_METRICS_ENABLED", "true").lower() == "true", ) return cls( diff --git a/src/tetra_rp/runtime/retry_manager.py b/src/runpod_flash/runtime/retry_manager.py similarity index 96% rename from src/tetra_rp/runtime/retry_manager.py rename to src/runpod_flash/runtime/retry_manager.py index 7435ca22..bc43d937 100644 --- a/src/tetra_rp/runtime/retry_manager.py +++ b/src/runpod_flash/runtime/retry_manager.py @@ -4,7 +4,7 @@ import logging from typing import Any, Callable, Optional, Set, Tuple, Type -from tetra_rp.core.utils.backoff import get_backoff_delay +from runpod_flash.core.utils.backoff import get_backoff_delay logger = logging.getLogger(__name__) @@ -62,7 +62,7 @@ async def retry_with_backoff( try: # Check circuit breaker before attempting if circuit_breaker is not None: - from tetra_rp.runtime.circuit_breaker import CircuitState + from runpod_flash.runtime.circuit_breaker import CircuitState if circuit_breaker.get_state() == CircuitState.OPEN: raise RuntimeError( diff --git a/src/tetra_rp/runtime/serialization.py b/src/runpod_flash/runtime/serialization.py similarity index 100% rename from src/tetra_rp/runtime/serialization.py rename to src/runpod_flash/runtime/serialization.py diff --git a/src/tetra_rp/runtime/service_registry.py b/src/runpod_flash/runtime/service_registry.py similarity index 99% rename from src/tetra_rp/runtime/service_registry.py rename to src/runpod_flash/runtime/service_registry.py index 05795b65..9de8ebd2 100644 --- a/src/tetra_rp/runtime/service_registry.py +++ b/src/runpod_flash/runtime/service_registry.py @@ -9,7 +9,7 @@ from typing import Dict, Optional from urllib.parse import urlparse -from tetra_rp.core.resources.serverless import ServerlessResource +from runpod_flash.core.resources.serverless import ServerlessResource from .config import DEFAULT_CACHE_TTL from .state_manager_client import StateManagerClient, ManifestServiceUnavailableError diff --git a/src/tetra_rp/runtime/state_manager_client.py b/src/runpod_flash/runtime/state_manager_client.py similarity index 99% rename from src/tetra_rp/runtime/state_manager_client.py rename to src/runpod_flash/runtime/state_manager_client.py index a8bc6890..d733a852 100644 --- a/src/tetra_rp/runtime/state_manager_client.py +++ b/src/runpod_flash/runtime/state_manager_client.py @@ -4,7 +4,7 @@ import logging from typing import Any, Dict, Optional -from tetra_rp.core.api.runpod import RunpodGraphQLClient +from runpod_flash.core.api.runpod import RunpodGraphQLClient from .config import DEFAULT_MAX_RETRIES from .exceptions import GraphQLError, ManifestServiceUnavailableError diff --git a/src/tetra_rp/stubs/__init__.py b/src/runpod_flash/stubs/__init__.py similarity index 100% rename from src/tetra_rp/stubs/__init__.py rename to src/runpod_flash/stubs/__init__.py diff --git a/src/tetra_rp/stubs/live_serverless.py b/src/runpod_flash/stubs/live_serverless.py similarity index 100% rename from src/tetra_rp/stubs/live_serverless.py rename to src/runpod_flash/stubs/live_serverless.py diff --git a/src/tetra_rp/stubs/load_balancer_sls.py b/src/runpod_flash/stubs/load_balancer_sls.py similarity index 99% rename from src/tetra_rp/stubs/load_balancer_sls.py rename to src/runpod_flash/stubs/load_balancer_sls.py index 61edcb3f..cc4ff468 100644 --- a/src/tetra_rp/stubs/load_balancer_sls.py +++ b/src/runpod_flash/stubs/load_balancer_sls.py @@ -10,8 +10,8 @@ import httpx -from tetra_rp.core.utils.http import get_authenticated_httpx_client -from tetra_rp.runtime.serialization import ( +from runpod_flash.core.utils.http import get_authenticated_httpx_client +from runpod_flash.runtime.serialization import ( deserialize_arg, serialize_args, serialize_kwargs, diff --git a/src/tetra_rp/stubs/registry.py b/src/runpod_flash/stubs/registry.py similarity index 100% rename from src/tetra_rp/stubs/registry.py rename to src/runpod_flash/stubs/registry.py diff --git a/src/tetra_rp/stubs/serverless.py b/src/runpod_flash/stubs/serverless.py similarity index 100% rename from src/tetra_rp/stubs/serverless.py rename to src/runpod_flash/stubs/serverless.py diff --git a/src/tetra_rp/cli/utils/skeleton_template/requirements.txt b/src/tetra_rp/cli/utils/skeleton_template/requirements.txt deleted file mode 100644 index 532a30b0..00000000 --- a/src/tetra_rp/cli/utils/skeleton_template/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -tetra_rp diff --git a/src/tetra_rp/config.py b/src/tetra_rp/config.py deleted file mode 100644 index ff347b8e..00000000 --- a/src/tetra_rp/config.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Configuration management for tetra-rp CLI.""" - -from pathlib import Path -from typing import NamedTuple - - -class TetraPaths(NamedTuple): - """Paths for tetra-rp configuration and data.""" - - tetra_dir: Path - config_file: Path - deployments_file: Path - - def ensure_tetra_dir(self) -> None: - """Ensure the .tetra directory exists.""" - self.tetra_dir.mkdir(exist_ok=True) - - -def get_paths() -> TetraPaths: - """Get standardized paths for tetra-rp configuration.""" - tetra_dir = Path.cwd() / ".tetra" - config_file = tetra_dir / "config.json" - deployments_file = tetra_dir / "deployments.json" - - return TetraPaths( - tetra_dir=tetra_dir, - config_file=config_file, - deployments_file=deployments_file, - ) diff --git a/tests/conftest.py b/tests/conftest.py index a481a628..19ddd55f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,5 @@ """ -Test configuration and fixtures for tetra-rp tests. +Test configuration and fixtures for flash tests. Provides shared fixtures for: - Resource configurations (GPU, CPU) @@ -17,8 +17,8 @@ import pytest -from tetra_rp.core.resources.resource_manager import ResourceManager -from tetra_rp.core.utils.singleton import SingletonMixin +from runpod_flash.core.resources.resource_manager import ResourceManager +from runpod_flash.core.utils.singleton import SingletonMixin def pytest_configure(config): @@ -267,7 +267,7 @@ def isolate_resource_state_file( Returns: Path to worker-specific state file. """ - from tetra_rp.core.resources import resource_manager + from runpod_flash.core.resources import resource_manager worker_state_file = worker_runpod_dir / "resources.pkl" monkeypatch.setattr(resource_manager, "RESOURCE_STATE_FILE", worker_state_file) @@ -328,8 +328,8 @@ def patched_reducer_override(self, obj): # Clear module-level caches (worker-isolated due to process boundaries) try: - from tetra_rp.stubs.live_serverless import _SERIALIZED_FUNCTION_CACHE - from tetra_rp.execute_class import _SERIALIZED_CLASS_CACHE + from runpod_flash.stubs.live_serverless import _SERIALIZED_FUNCTION_CACHE + from runpod_flash.execute_class import _SERIALIZED_CLASS_CACHE _SERIALIZED_FUNCTION_CACHE.clear() _SERIALIZED_CLASS_CACHE.clear() @@ -360,8 +360,8 @@ def patched_reducer_override(self, obj): # Cleanup after test try: - from tetra_rp.stubs.live_serverless import _SERIALIZED_FUNCTION_CACHE - from tetra_rp.execute_class import _SERIALIZED_CLASS_CACHE + from runpod_flash.stubs.live_serverless import _SERIALIZED_FUNCTION_CACHE + from runpod_flash.execute_class import _SERIALIZED_CLASS_CACHE _SERIALIZED_FUNCTION_CACHE.clear() _SERIALIZED_CLASS_CACHE.clear() diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py index 37387858..0feda7b7 100644 --- a/tests/integration/__init__.py +++ b/tests/integration/__init__.py @@ -1 +1 @@ -# Integration tests for tetra-rp +# Integration tests for flash diff --git a/tests/integration/test_class_execution_integration.py b/tests/integration/test_class_execution_integration.py index 374e32af..17fb6e36 100644 --- a/tests/integration/test_class_execution_integration.py +++ b/tests/integration/test_class_execution_integration.py @@ -1,5 +1,5 @@ """ -Integration tests for tetra_rp remote class execution functionality. +Integration tests for runpod_flash remote class execution functionality. These tests verify end-to-end functionality of remote class execution including: - Remote class decorator integration @@ -14,9 +14,9 @@ import cloudpickle import pytest -from tetra_rp.client import remote -from tetra_rp.core.resources import ServerlessResource -from tetra_rp.execute_class import create_remote_class +from runpod_flash.client import remote +from runpod_flash.core.resources import ServerlessResource +from runpod_flash.execute_class import create_remote_class class TestRemoteClassDecoratorIntegration: @@ -640,10 +640,10 @@ async def mock_ensure_initialized(): ): # The error should occur during method call when trying to serialize # Mock cloudpickle.dumps to raise an error - from tetra_rp.runtime.exceptions import SerializationError + from runpod_flash.runtime.exceptions import SerializationError with patch( - "tetra_rp.runtime.serialization.cloudpickle.dumps", + "runpod_flash.runtime.serialization.cloudpickle.dumps", side_effect=TypeError("Can't pickle file objects"), ): with pytest.raises( diff --git a/tests/integration/test_cpu_disk_sizing.py b/tests/integration/test_cpu_disk_sizing.py index 0d10d444..e9032850 100644 --- a/tests/integration/test_cpu_disk_sizing.py +++ b/tests/integration/test_cpu_disk_sizing.py @@ -7,11 +7,14 @@ """ import pytest -from tetra_rp.core.resources.cpu import CpuInstanceType -from tetra_rp.core.resources.serverless import ServerlessEndpoint -from tetra_rp.core.resources.serverless_cpu import CpuServerlessEndpoint -from tetra_rp.core.resources.live_serverless import LiveServerless, CpuLiveServerless -from tetra_rp.core.resources.template import PodTemplate +from runpod_flash.core.resources.cpu import CpuInstanceType +from runpod_flash.core.resources.serverless import ServerlessEndpoint +from runpod_flash.core.resources.serverless_cpu import CpuServerlessEndpoint +from runpod_flash.core.resources.live_serverless import ( + LiveServerless, + CpuLiveServerless, +) +from runpod_flash.core.resources.template import PodTemplate class TestUniversalCpuDetectionIntegration: @@ -126,7 +129,7 @@ def test_live_serverless_cpu_integration(self): # 2. CPU utilities calculate minimum disk size # 3. Template creation with auto-sizing # 4. Validation passes - assert "tetra-rp-cpu:" in live_serverless.imageName + assert "flash-cpu:" in live_serverless.imageName assert live_serverless.instanceIds == [ CpuInstanceType.CPU5C_1_2, CpuInstanceType.CPU5C_2_4, @@ -251,8 +254,8 @@ def test_live_serverless_image_consistency(self): # Verify different images are used assert gpu_live.imageName != cpu_live.imageName - assert "tetra-rp:" in gpu_live.imageName - assert "tetra-rp-cpu:" in cpu_live.imageName + assert "flash:" in gpu_live.imageName + assert "flash-cpu:" in cpu_live.imageName # Verify images remain locked despite attempts to change original_gpu_image = gpu_live.imageName diff --git a/tests/integration/test_cross_endpoint_routing.py b/tests/integration/test_cross_endpoint_routing.py index 43b3cd03..f7a1af26 100644 --- a/tests/integration/test_cross_endpoint_routing.py +++ b/tests/integration/test_cross_endpoint_routing.py @@ -7,13 +7,13 @@ import pytest -from tetra_rp.runtime.production_wrapper import ( +from runpod_flash.runtime.production_wrapper import ( ProductionWrapper, create_production_wrapper, reset_wrapper, ) -from tetra_rp.runtime.service_registry import ServiceRegistry -from tetra_rp.runtime.state_manager_client import StateManagerClient +from runpod_flash.runtime.service_registry import ServiceRegistry +from runpod_flash.runtime.state_manager_client import StateManagerClient class TestCrossEndpointRoutingIntegration: diff --git a/tests/integration/test_lb_remote_execution.py b/tests/integration/test_lb_remote_execution.py index 76c30c9a..406e1521 100644 --- a/tests/integration/test_lb_remote_execution.py +++ b/tests/integration/test_lb_remote_execution.py @@ -10,7 +10,7 @@ import cloudpickle -from tetra_rp import remote, LiveLoadBalancer, LoadBalancerSlsResource +from runpod_flash import remote, LiveLoadBalancer, LoadBalancerSlsResource class TestRemoteWithLoadBalancerIntegration: @@ -62,7 +62,7 @@ async def bad_path(): @pytest.mark.asyncio async def test_remote_function_serialization_roundtrip(self): """Test that function code and args serialize/deserialize correctly.""" - from tetra_rp.stubs.load_balancer_sls import LoadBalancerSlsStub + from runpod_flash.stubs.load_balancer_sls import LoadBalancerSlsStub mock_resource = MagicMock() stub = LoadBalancerSlsStub(mock_resource) @@ -88,7 +88,7 @@ def add(x: int, y: int) -> int: @pytest.mark.asyncio async def test_stub_response_deserialization(self): """Test that response deserialization works correctly.""" - from tetra_rp.stubs.load_balancer_sls import LoadBalancerSlsStub + from runpod_flash.stubs.load_balancer_sls import LoadBalancerSlsStub mock_resource = MagicMock() stub = LoadBalancerSlsStub(mock_resource) @@ -114,26 +114,26 @@ async def echo(message: str): # Verify resource is correctly configured # Note: name may have "-fb" appended by flash boot validator assert "test-live-api" in lb.name - assert "tetra-rp-lb" in lb.imageName + assert "flash-lb" in lb.imageName assert echo.__remote_config__["method"] == "POST" def test_live_load_balancer_image_locked(self): - """Test that LiveLoadBalancer locks the image to Tetra LB image.""" + """Test that LiveLoadBalancer locks the image to Flash LB image.""" lb = LiveLoadBalancer(name="test-api") # Verify image is locked and cannot be overridden original_image = lb.imageName - assert "tetra-rp-lb" in original_image + assert "flash-lb" in original_image # Try to set a different image (should be ignored due to property) lb.imageName = "custom-image:latest" - # Image should still be locked to Tetra + # Image should still be locked to Flash assert lb.imageName == original_image def test_load_balancer_vs_queue_based_endpoints(self): """Test that LB and QB endpoints have different characteristics.""" - from tetra_rp import ServerlessEndpoint + from runpod_flash import ServerlessEndpoint lb = LoadBalancerSlsResource(name="lb-api", imageName="test:latest") qb = ServerlessEndpoint(name="qb-api", imageName="test:latest") @@ -160,13 +160,13 @@ async def qb_func(): def test_scanner_discovers_load_balancer_resources(self): """Test that scanner can discover LiveLoadBalancer and LoadBalancerSlsResource.""" - from tetra_rp.cli.commands.build_utils.scanner import RemoteDecoratorScanner + from runpod_flash.cli.commands.build_utils.scanner import RemoteDecoratorScanner from pathlib import Path import tempfile # Create temporary Python file with LoadBalancer resource code = """ -from tetra_rp import LiveLoadBalancer, LoadBalancerSlsResource, remote +from runpod_flash import LiveLoadBalancer, LoadBalancerSlsResource, remote # Test LiveLoadBalancer discovery api = LiveLoadBalancer(name="test-api") diff --git a/tests/integration/test_mothership_provisioning.py b/tests/integration/test_mothership_provisioning.py index 16b2b5c8..9436e995 100644 --- a/tests/integration/test_mothership_provisioning.py +++ b/tests/integration/test_mothership_provisioning.py @@ -6,11 +6,11 @@ import pytest -from tetra_rp.runtime.mothership_provisioner import ( +from runpod_flash.runtime.mothership_provisioner import ( compute_resource_hash, reconcile_children, ) -from tetra_rp.runtime.state_manager_client import StateManagerClient +from runpod_flash.runtime.state_manager_client import StateManagerClient class TestMothershipProvisioningFlow: @@ -52,9 +52,11 @@ async def test_reconcile_children_first_boot(self): mock_cpu_resource.endpoint_url = "https://cpu-worker.api.runpod.ai" with ( - patch("tetra_rp.runtime.mothership_provisioner.load_manifest") as mock_load, patch( - "tetra_rp.runtime.mothership_provisioner.ResourceManager" + "runpod_flash.runtime.mothership_provisioner.load_manifest" + ) as mock_load, + patch( + "runpod_flash.runtime.mothership_provisioner.ResourceManager" ) as mock_rm_class, patch.dict( "os.environ", @@ -135,9 +137,11 @@ async def test_reconcile_children_with_changes(self): mock_gpu_resource.endpoint_url = "https://gpu-worker.api.runpod.ai" with ( - patch("tetra_rp.runtime.mothership_provisioner.load_manifest") as mock_load, patch( - "tetra_rp.runtime.mothership_provisioner.ResourceManager" + "runpod_flash.runtime.mothership_provisioner.load_manifest" + ) as mock_load, + patch( + "runpod_flash.runtime.mothership_provisioner.ResourceManager" ) as mock_rm_class, patch.dict( "os.environ", @@ -214,9 +218,11 @@ async def test_reconcile_children_with_removed_resources(self): mock_gpu_resource.endpoint_url = "https://gpu-worker.api.runpod.ai" with ( - patch("tetra_rp.runtime.mothership_provisioner.load_manifest") as mock_load, patch( - "tetra_rp.runtime.mothership_provisioner.ResourceManager" + "runpod_flash.runtime.mothership_provisioner.load_manifest" + ) as mock_load, + patch( + "runpod_flash.runtime.mothership_provisioner.ResourceManager" ) as mock_rm_class, patch.dict( "os.environ", @@ -288,12 +294,14 @@ async def test_reconcile_children_deploys_load_balancer_resources(self): mock_gpu_resource.endpoint_url = "https://gpu-worker.api.runpod.ai" with ( - patch("tetra_rp.runtime.mothership_provisioner.load_manifest") as mock_load, patch( - "tetra_rp.runtime.mothership_provisioner.create_resource_from_manifest" + "runpod_flash.runtime.mothership_provisioner.load_manifest" + ) as mock_load, + patch( + "runpod_flash.runtime.mothership_provisioner.create_resource_from_manifest" ) as mock_create, patch( - "tetra_rp.runtime.mothership_provisioner.ResourceManager" + "runpod_flash.runtime.mothership_provisioner.ResourceManager" ) as mock_rm_class, patch.dict( "os.environ", @@ -362,9 +370,11 @@ async def test_reconcile_children_handles_deployment_errors(self): mock_cpu_resource.endpoint_url = "https://cpu-worker.api.runpod.ai" with ( - patch("tetra_rp.runtime.mothership_provisioner.load_manifest") as mock_load, patch( - "tetra_rp.runtime.mothership_provisioner.ResourceManager" + "runpod_flash.runtime.mothership_provisioner.load_manifest" + ) as mock_load, + patch( + "runpod_flash.runtime.mothership_provisioner.ResourceManager" ) as mock_rm_class, patch.dict( "os.environ", @@ -448,9 +458,11 @@ async def test_idempotent_provisioning_on_second_boot(self): # Mock ResourceManager - should not be called with ( - patch("tetra_rp.runtime.mothership_provisioner.load_manifest") as mock_load, patch( - "tetra_rp.runtime.mothership_provisioner.ResourceManager" + "runpod_flash.runtime.mothership_provisioner.load_manifest" + ) as mock_load, + patch( + "runpod_flash.runtime.mothership_provisioner.ResourceManager" ) as mock_rm_class, patch.dict( "os.environ", diff --git a/tests/integration/test_remote_concurrency.py b/tests/integration/test_remote_concurrency.py index dc39f188..f8052fc3 100644 --- a/tests/integration/test_remote_concurrency.py +++ b/tests/integration/test_remote_concurrency.py @@ -16,16 +16,19 @@ import pytest -from tetra_rp import remote -from tetra_rp.core.resources.resource_manager import ( +from runpod_flash import remote +from runpod_flash.core.resources.resource_manager import ( ResourceManager, RESOURCE_STATE_FILE, ) -from tetra_rp.core.resources.serverless_cpu import CpuServerlessEndpoint -from tetra_rp.core.resources.serverless import JobOutput, ServerlessEndpoint -from tetra_rp.core.resources.live_serverless import LiveServerless, CpuLiveServerless -from tetra_rp.core.utils.singleton import SingletonMixin -from tetra_rp.protos.remote_execution import FunctionResponse +from runpod_flash.core.resources.serverless_cpu import CpuServerlessEndpoint +from runpod_flash.core.resources.serverless import JobOutput, ServerlessEndpoint +from runpod_flash.core.resources.live_serverless import ( + LiveServerless, + CpuLiveServerless, +) +from runpod_flash.core.utils.singleton import SingletonMixin +from runpod_flash.protos.remote_execution import FunctionResponse @pytest.mark.serial @@ -47,14 +50,14 @@ def setup_method(self): self.original_state_file = RESOURCE_STATE_FILE # Patch the state file location - import tetra_rp.core.resources.resource_manager as rm_module + import runpod_flash.core.resources.resource_manager as rm_module rm_module.RESOURCE_STATE_FILE = Path(self.temp_dir) / "test_resources.pkl" def teardown_method(self): """Clean up test environment.""" # Restore original state file - import tetra_rp.core.resources.resource_manager as rm_module + import runpod_flash.core.resources.resource_manager as rm_module rm_module.RESOURCE_STATE_FILE = self.original_state_file @@ -111,7 +114,7 @@ def test_function(value: str) -> Dict[str, Any]: # Mock the actual function execution to avoid network calls with patch( - "tetra_rp.stubs.serverless.ServerlessEndpointStub.execute", + "runpod_flash.stubs.serverless.ServerlessEndpointStub.execute", return_value=JobOutput( id="mock-job-1", workerId="mock-worker-1", @@ -205,7 +208,7 @@ def function_2(value: str) -> str: # Mock the actual function execution to avoid network calls with patch( - "tetra_rp.stubs.serverless.ServerlessEndpointStub.execute", + "runpod_flash.stubs.serverless.ServerlessEndpointStub.execute", return_value=JobOutput( id="mock-job-2", workerId="mock-worker-2", @@ -272,7 +275,7 @@ def test_function(value: int) -> int: # Mock the actual function execution to avoid network calls with patch( - "tetra_rp.stubs.serverless.ServerlessEndpointStub.execute", + "runpod_flash.stubs.serverless.ServerlessEndpointStub.execute", return_value=JobOutput( id="mock-job-3", workerId="mock-worker-3", @@ -350,7 +353,7 @@ def test_function(value: str) -> str: # Mock the actual function execution to avoid network calls with patch( - "tetra_rp.stubs.serverless.ServerlessEndpointStub.execute", + "runpod_flash.stubs.serverless.ServerlessEndpointStub.execute", return_value=JobOutput( id="mock-job-4", workerId="mock-worker-4", @@ -438,7 +441,7 @@ def test_function(value: str) -> Dict[str, Any]: # Mock the actual function execution to avoid network calls with patch( - "tetra_rp.stubs.serverless.ServerlessEndpointStub.execute", + "runpod_flash.stubs.serverless.ServerlessEndpointStub.execute", return_value=JobOutput( id="mock-job-serverless", workerId="mock-worker-serverless", @@ -535,7 +538,7 @@ def test_function(value: str) -> str: # Mock the actual function execution to avoid network calls with patch( - "tetra_rp.stubs.serverless.ServerlessEndpointStub.execute", + "runpod_flash.stubs.serverless.ServerlessEndpointStub.execute", return_value=JobOutput( id="mock-job-serverless-race", workerId="mock-worker-serverless-race", @@ -626,7 +629,7 @@ def test_function(value: str) -> Dict[str, Any]: mock_result = {"result": "mocked_response"} with ( patch( - "tetra_rp.stubs.live_serverless.LiveServerlessStub.ExecuteFunction", + "runpod_flash.stubs.live_serverless.LiveServerlessStub.ExecuteFunction", return_value=FunctionResponse( success=True, result=base64.b64encode(cloudpickle.dumps(mock_result)).decode( @@ -637,7 +640,7 @@ def test_function(value: str) -> Dict[str, Any]: ), ), patch( - "tetra_rp.stubs.live_serverless.get_function_source", + "runpod_flash.stubs.live_serverless.get_function_source", return_value=( "def test_function(value):\n return {'result': f'processed_{value}'}", "mock_hash", @@ -732,7 +735,7 @@ def test_function(value: str) -> str: mock_result = "mocked_result" with ( patch( - "tetra_rp.stubs.live_serverless.LiveServerlessStub.ExecuteFunction", + "runpod_flash.stubs.live_serverless.LiveServerlessStub.ExecuteFunction", return_value=FunctionResponse( success=True, result=base64.b64encode(cloudpickle.dumps(mock_result)).decode( @@ -743,7 +746,7 @@ def test_function(value: str) -> str: ), ), patch( - "tetra_rp.stubs.live_serverless.get_function_source", + "runpod_flash.stubs.live_serverless.get_function_source", return_value=( "def test_function(value):\n return f'result_{value}'", "mock_hash", @@ -830,7 +833,7 @@ def test_function(value: str) -> Dict[str, Any]: mock_result = {"result": "mocked_response"} with ( patch( - "tetra_rp.stubs.live_serverless.LiveServerlessStub.ExecuteFunction", + "runpod_flash.stubs.live_serverless.LiveServerlessStub.ExecuteFunction", return_value=FunctionResponse( success=True, result=base64.b64encode(cloudpickle.dumps(mock_result)).decode( @@ -841,7 +844,7 @@ def test_function(value: str) -> Dict[str, Any]: ), ), patch( - "tetra_rp.stubs.live_serverless.get_function_source", + "runpod_flash.stubs.live_serverless.get_function_source", return_value=( "def test_function(value):\n return {'result': f'processed_{value}'}", "mock_hash", @@ -936,7 +939,7 @@ def test_function(value: str) -> str: mock_result = "mocked_result" with ( patch( - "tetra_rp.stubs.live_serverless.LiveServerlessStub.ExecuteFunction", + "runpod_flash.stubs.live_serverless.LiveServerlessStub.ExecuteFunction", return_value=FunctionResponse( success=True, result=base64.b64encode(cloudpickle.dumps(mock_result)).decode( @@ -947,7 +950,7 @@ def test_function(value: str) -> str: ), ), patch( - "tetra_rp.stubs.live_serverless.get_function_source", + "runpod_flash.stubs.live_serverless.get_function_source", return_value=( "def test_function(value):\n return f'result_{value}'", "mock_hash", diff --git a/tests/integration/test_run_auto_provision.py b/tests/integration/test_run_auto_provision.py index c07d8634..9478f442 100644 --- a/tests/integration/test_run_auto_provision.py +++ b/tests/integration/test_run_auto_provision.py @@ -5,7 +5,7 @@ from textwrap import dedent from typer.testing import CliRunner -from tetra_rp.cli.main import app +from runpod_flash.cli.main import app runner = CliRunner() @@ -22,8 +22,8 @@ def temp_project(self, tmp_path): dedent( """ from fastapi import FastAPI - from tetra_rp.client import remote - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.client import remote + from runpod_flash.core.resources.serverless import ServerlessResource app = FastAPI() @@ -56,8 +56,8 @@ def temp_project_many_resources(self, tmp_path): dedent( """ from fastapi import FastAPI - from tetra_rp.client import remote - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.client import remote + from runpod_flash.core.resources.serverless import ServerlessResource app = FastAPI() @@ -105,20 +105,20 @@ def test_run_without_auto_provision(self, temp_project, monkeypatch): monkeypatch.chdir(temp_project) # Mock subprocess to prevent actual uvicorn start - with patch("tetra_rp.cli.commands.run.subprocess.Popen") as mock_popen: + with patch("runpod_flash.cli.commands.run.subprocess.Popen") as mock_popen: mock_process = MagicMock() mock_process.pid = 12345 mock_process.wait.side_effect = KeyboardInterrupt() mock_popen.return_value = mock_process # Mock OS-level process group operations to prevent hanging - with patch("tetra_rp.cli.commands.run.os.getpgid") as mock_getpgid: + with patch("runpod_flash.cli.commands.run.os.getpgid") as mock_getpgid: mock_getpgid.return_value = 12345 - with patch("tetra_rp.cli.commands.run.os.killpg"): + with patch("runpod_flash.cli.commands.run.os.killpg"): # Mock discovery to track if it was called with patch( - "tetra_rp.cli.commands.run._discover_resources" + "runpod_flash.cli.commands.run._discover_resources" ) as mock_discover: runner.invoke(app, ["run"]) @@ -130,20 +130,20 @@ def test_run_with_auto_provision_single_resource(self, temp_project, monkeypatch monkeypatch.chdir(temp_project) # Mock subprocess to prevent actual uvicorn start - with patch("tetra_rp.cli.commands.run.subprocess.Popen") as mock_popen: + with patch("runpod_flash.cli.commands.run.subprocess.Popen") as mock_popen: mock_process = MagicMock() mock_process.pid = 12345 mock_process.wait.side_effect = KeyboardInterrupt() mock_popen.return_value = mock_process # Mock OS-level process group operations - with patch("tetra_rp.cli.commands.run.os.getpgid") as mock_getpgid: + with patch("runpod_flash.cli.commands.run.os.getpgid") as mock_getpgid: mock_getpgid.return_value = 12345 - with patch("tetra_rp.cli.commands.run.os.killpg"): + with patch("runpod_flash.cli.commands.run.os.killpg"): # Mock deployment orchestrator with patch( - "tetra_rp.cli.commands.run._provision_resources" + "runpod_flash.cli.commands.run._provision_resources" ) as mock_provision: runner.invoke(app, ["run", "--auto-provision"]) @@ -158,20 +158,20 @@ def test_run_with_auto_provision_skips_reload(self, temp_project, monkeypatch): monkeypatch.setenv("UVICORN_RELOADER_PID", "12345") # Mock subprocess to prevent actual uvicorn start - with patch("tetra_rp.cli.commands.run.subprocess.Popen") as mock_popen: + with patch("runpod_flash.cli.commands.run.subprocess.Popen") as mock_popen: mock_process = MagicMock() mock_process.pid = 12345 mock_process.wait.side_effect = KeyboardInterrupt() mock_popen.return_value = mock_process # Mock OS-level process group operations - with patch("tetra_rp.cli.commands.run.os.getpgid") as mock_getpgid: + with patch("runpod_flash.cli.commands.run.os.getpgid") as mock_getpgid: mock_getpgid.return_value = 12345 - with patch("tetra_rp.cli.commands.run.os.killpg"): + with patch("runpod_flash.cli.commands.run.os.killpg"): # Mock provisioning with patch( - "tetra_rp.cli.commands.run._provision_resources" + "runpod_flash.cli.commands.run._provision_resources" ) as mock_provision: runner.invoke(app, ["run", "--auto-provision"]) @@ -188,31 +188,31 @@ def test_run_with_auto_provision_many_resources_confirmed( mock_resources = [MagicMock(name=f"endpoint-{i}") for i in range(6)] # Mock subprocess to prevent actual uvicorn start - with patch("tetra_rp.cli.commands.run.subprocess.Popen") as mock_popen: + with patch("runpod_flash.cli.commands.run.subprocess.Popen") as mock_popen: mock_process = MagicMock() mock_process.pid = 12345 mock_process.wait.side_effect = KeyboardInterrupt() mock_popen.return_value = mock_process # Mock OS-level process group operations - with patch("tetra_rp.cli.commands.run.os.getpgid") as mock_getpgid: + with patch("runpod_flash.cli.commands.run.os.getpgid") as mock_getpgid: mock_getpgid.return_value = 12345 - with patch("tetra_rp.cli.commands.run.os.killpg"): + with patch("runpod_flash.cli.commands.run.os.killpg"): # Mock discovery to return > 5 resources with patch( - "tetra_rp.cli.commands.run._discover_resources" + "runpod_flash.cli.commands.run._discover_resources" ) as mock_discover: mock_discover.return_value = mock_resources # Mock questionary to simulate user confirmation with patch( - "tetra_rp.cli.commands.run.questionary.confirm" + "runpod_flash.cli.commands.run.questionary.confirm" ) as mock_confirm: mock_confirm.return_value.ask.return_value = True with patch( - "tetra_rp.cli.commands.run._provision_resources" + "runpod_flash.cli.commands.run._provision_resources" ) as mock_provision: runner.invoke(app, ["run", "--auto-provision"]) @@ -232,31 +232,31 @@ def test_run_with_auto_provision_many_resources_cancelled( mock_resources = [MagicMock(name=f"endpoint-{i}") for i in range(6)] # Mock subprocess to prevent actual uvicorn start - with patch("tetra_rp.cli.commands.run.subprocess.Popen") as mock_popen: + with patch("runpod_flash.cli.commands.run.subprocess.Popen") as mock_popen: mock_process = MagicMock() mock_process.pid = 12345 mock_process.wait.side_effect = KeyboardInterrupt() mock_popen.return_value = mock_process # Mock OS-level process group operations - with patch("tetra_rp.cli.commands.run.os.getpgid") as mock_getpgid: + with patch("runpod_flash.cli.commands.run.os.getpgid") as mock_getpgid: mock_getpgid.return_value = 12345 - with patch("tetra_rp.cli.commands.run.os.killpg"): + with patch("runpod_flash.cli.commands.run.os.killpg"): # Mock discovery to return > 5 resources with patch( - "tetra_rp.cli.commands.run._discover_resources" + "runpod_flash.cli.commands.run._discover_resources" ) as mock_discover: mock_discover.return_value = mock_resources # Mock questionary to simulate user cancellation with patch( - "tetra_rp.cli.commands.run.questionary.confirm" + "runpod_flash.cli.commands.run.questionary.confirm" ) as mock_confirm: mock_confirm.return_value.ask.return_value = False with patch( - "tetra_rp.cli.commands.run._provision_resources" + "runpod_flash.cli.commands.run._provision_resources" ) as mock_provision: runner.invoke(app, ["run", "--auto-provision"]) @@ -271,20 +271,20 @@ def test_run_auto_provision_discovery_error(self, temp_project, monkeypatch): monkeypatch.chdir(temp_project) # Mock subprocess to prevent actual uvicorn start - with patch("tetra_rp.cli.commands.run.subprocess.Popen") as mock_popen: + with patch("runpod_flash.cli.commands.run.subprocess.Popen") as mock_popen: mock_process = MagicMock() mock_process.pid = 12345 mock_process.wait.side_effect = KeyboardInterrupt() mock_popen.return_value = mock_process # Mock OS-level process group operations - with patch("tetra_rp.cli.commands.run.os.getpgid") as mock_getpgid: + with patch("runpod_flash.cli.commands.run.os.getpgid") as mock_getpgid: mock_getpgid.return_value = 12345 - with patch("tetra_rp.cli.commands.run.os.killpg"): + with patch("runpod_flash.cli.commands.run.os.killpg"): # Mock discovery to raise exception with patch( - "tetra_rp.cli.commands.run._discover_resources" + "runpod_flash.cli.commands.run._discover_resources" ) as mock_discover: mock_discover.return_value = [] @@ -314,19 +314,19 @@ def root(): ) # Mock subprocess to prevent actual uvicorn start - with patch("tetra_rp.cli.commands.run.subprocess.Popen") as mock_popen: + with patch("runpod_flash.cli.commands.run.subprocess.Popen") as mock_popen: mock_process = MagicMock() mock_process.pid = 12345 mock_process.wait.side_effect = KeyboardInterrupt() mock_popen.return_value = mock_process # Mock OS-level process group operations - with patch("tetra_rp.cli.commands.run.os.getpgid") as mock_getpgid: + with patch("runpod_flash.cli.commands.run.os.getpgid") as mock_getpgid: mock_getpgid.return_value = 12345 - with patch("tetra_rp.cli.commands.run.os.killpg"): + with patch("runpod_flash.cli.commands.run.os.killpg"): with patch( - "tetra_rp.cli.commands.run._provision_resources" + "runpod_flash.cli.commands.run._provision_resources" ) as mock_provision: runner.invoke(app, ["run", "--auto-provision"]) diff --git a/tests/unit/cli/commands/build_utils/test_handler_generator.py b/tests/unit/cli/commands/build_utils/test_handler_generator.py new file mode 100644 index 00000000..181e5e2d --- /dev/null +++ b/tests/unit/cli/commands/build_utils/test_handler_generator.py @@ -0,0 +1,261 @@ +"""Tests for HandlerGenerator.""" + +import tempfile +from pathlib import Path + + +from runpod_flash.cli.commands.build_utils.handler_generator import HandlerGenerator + + +def test_generate_handlers_creates_files(): + """Test that handler generator creates handler files.""" + with tempfile.TemporaryDirectory() as tmpdir: + build_dir = Path(tmpdir) + + manifest = { + "version": "1.0", + "generated_at": "2026-01-02T10:00:00Z", + "project_name": "test_app", + "resources": { + "gpu_config": { + "resource_type": "LiveServerless", + "handler_file": "handler_gpu_config.py", + "functions": [ + { + "name": "gpu_task", + "module": "workers.gpu", + "is_async": True, + "is_class": False, + } + ], + } + }, + } + + generator = HandlerGenerator(manifest, build_dir) + handler_paths = generator.generate_handlers() + + assert len(handler_paths) == 1 + assert handler_paths[0].exists() + assert handler_paths[0].name == "handler_gpu_config.py" + + +def test_handler_file_contains_imports(): + """Test that generated handler includes proper imports.""" + with tempfile.TemporaryDirectory() as tmpdir: + build_dir = Path(tmpdir) + + manifest = { + "version": "1.0", + "generated_at": "2026-01-02T10:00:00Z", + "project_name": "test_app", + "resources": { + "gpu_config": { + "resource_type": "LiveServerless", + "handler_file": "handler_gpu_config.py", + "functions": [ + { + "name": "gpu_task", + "module": "workers.gpu", + "is_async": True, + "is_class": False, + }, + { + "name": "process_data", + "module": "workers.utils", + "is_async": False, + "is_class": False, + }, + ], + } + }, + } + + generator = HandlerGenerator(manifest, build_dir) + handler_paths = generator.generate_handlers() + + handler_content = handler_paths[0].read_text() + assert ( + "gpu_task = importlib.import_module('workers.gpu').gpu_task" + in handler_content + ) + assert ( + "process_data = importlib.import_module('workers.utils').process_data" + in handler_content + ) + + +def test_handler_file_contains_registry(): + """Test that generated handler includes function registry.""" + with tempfile.TemporaryDirectory() as tmpdir: + build_dir = Path(tmpdir) + + manifest = { + "version": "1.0", + "generated_at": "2026-01-02T10:00:00Z", + "project_name": "test_app", + "resources": { + "gpu_config": { + "resource_type": "LiveServerless", + "handler_file": "handler_gpu_config.py", + "functions": [ + { + "name": "gpu_task", + "module": "workers.gpu", + "is_async": True, + "is_class": False, + } + ], + } + }, + } + + generator = HandlerGenerator(manifest, build_dir) + handler_paths = generator.generate_handlers() + + handler_content = handler_paths[0].read_text() + assert "FUNCTION_REGISTRY = {" in handler_content + assert '"gpu_task": gpu_task,' in handler_content + + +def test_handler_file_contains_runpod_start(): + """Test that generated handler includes RunPod start.""" + with tempfile.TemporaryDirectory() as tmpdir: + build_dir = Path(tmpdir) + + manifest = { + "version": "1.0", + "generated_at": "2026-01-02T10:00:00Z", + "project_name": "test_app", + "resources": { + "test_config": { + "resource_type": "LiveServerless", + "handler_file": "handler_test_config.py", + "functions": [], + } + }, + } + + generator = HandlerGenerator(manifest, build_dir) + handler_paths = generator.generate_handlers() + + handler_content = handler_paths[0].read_text() + assert 'runpod.serverless.start({"handler": handler})' in handler_content + + +def test_multiple_handlers_created(): + """Test that multiple handlers are created for multiple resources.""" + with tempfile.TemporaryDirectory() as tmpdir: + build_dir = Path(tmpdir) + + manifest = { + "version": "1.0", + "generated_at": "2026-01-02T10:00:00Z", + "project_name": "test_app", + "resources": { + "gpu_config": { + "resource_type": "LiveServerless", + "handler_file": "handler_gpu_config.py", + "functions": [ + { + "name": "gpu_task", + "module": "workers.gpu", + "is_async": True, + "is_class": False, + } + ], + }, + "cpu_config": { + "resource_type": "CpuLiveServerless", + "handler_file": "handler_cpu_config.py", + "functions": [ + { + "name": "cpu_task", + "module": "workers.cpu", + "is_async": True, + "is_class": False, + } + ], + }, + }, + } + + generator = HandlerGenerator(manifest, build_dir) + handler_paths = generator.generate_handlers() + + assert len(handler_paths) == 2 + handler_names = {p.name for p in handler_paths} + assert handler_names == {"handler_gpu_config.py", "handler_cpu_config.py"} + + +def test_handler_includes_create_handler_import(): + """Test that generated handler imports create_handler factory.""" + with tempfile.TemporaryDirectory() as tmpdir: + build_dir = Path(tmpdir) + + manifest = { + "version": "1.0", + "generated_at": "2026-01-02T10:00:00Z", + "project_name": "test_app", + "resources": { + "test_config": { + "resource_type": "LiveServerless", + "handler_file": "handler_test_config.py", + "functions": [ + { + "name": "test_func", + "module": "workers.test", + "is_async": True, + "is_class": False, + } + ], + } + }, + } + + generator = HandlerGenerator(manifest, build_dir) + handler_paths = generator.generate_handlers() + + handler_content = handler_paths[0].read_text() + assert ( + "from runpod_flash.runtime.generic_handler import create_handler" + in handler_content + ) + assert "handler = create_handler(FUNCTION_REGISTRY)" in handler_content + + +def test_handler_does_not_contain_serialization_logic(): + """Test that generated handler delegates serialization to generic_handler.""" + with tempfile.TemporaryDirectory() as tmpdir: + build_dir = Path(tmpdir) + + manifest = { + "version": "1.0", + "generated_at": "2026-01-02T10:00:00Z", + "project_name": "test_app", + "resources": { + "test_config": { + "resource_type": "LiveServerless", + "handler_file": "handler_test_config.py", + "functions": [ + { + "name": "test_func", + "module": "workers.test", + "is_async": True, + "is_class": False, + } + ], + } + }, + } + + generator = HandlerGenerator(manifest, build_dir) + handler_paths = generator.generate_handlers() + + handler_content = handler_paths[0].read_text() + # Serialization logic should NOT be in generated handler + # (it's now in generic_handler.py) + assert "cloudpickle.loads(base64.b64decode" not in handler_content + assert "def handler(" not in handler_content + assert "import base64" not in handler_content + assert "import json" not in handler_content diff --git a/tests/unit/cli/commands/build_utils/test_manifest.py b/tests/unit/cli/commands/build_utils/test_manifest.py index 9d7c996c..8e639e0c 100644 --- a/tests/unit/cli/commands/build_utils/test_manifest.py +++ b/tests/unit/cli/commands/build_utils/test_manifest.py @@ -5,8 +5,8 @@ from pathlib import Path -from tetra_rp.cli.commands.build_utils.manifest import ManifestBuilder -from tetra_rp.cli.commands.build_utils.scanner import RemoteFunctionMetadata +from runpod_flash.cli.commands.build_utils.manifest import ManifestBuilder +from runpod_flash.cli.commands.build_utils.scanner import RemoteFunctionMetadata def test_build_manifest_single_resource(): diff --git a/tests/unit/cli/commands/build_utils/test_manifest_mothership.py b/tests/unit/cli/commands/build_utils/test_manifest_mothership.py index 60c437b2..8ee88c17 100644 --- a/tests/unit/cli/commands/build_utils/test_manifest_mothership.py +++ b/tests/unit/cli/commands/build_utils/test_manifest_mothership.py @@ -4,11 +4,11 @@ from pathlib import Path from unittest.mock import patch -from tetra_rp.cli.commands.build_utils.manifest import ManifestBuilder -from tetra_rp.cli.commands.build_utils.scanner import RemoteFunctionMetadata -from tetra_rp.core.resources.constants import ( - TETRA_CPU_LB_IMAGE, - TETRA_LB_IMAGE, +from runpod_flash.cli.commands.build_utils.manifest import ManifestBuilder +from runpod_flash.cli.commands.build_utils.scanner import RemoteFunctionMetadata +from runpod_flash.core.resources.constants import ( + FLASH_CPU_LB_IMAGE, + FLASH_LB_IMAGE, ) @@ -37,8 +37,8 @@ def root(): func_file = project_root / "functions.py" func_file.write_text( """ -from tetra_rp import remote -from tetra_rp import LiveServerless +from runpod_flash import remote +from runpod_flash import LiveServerless gpu_config = LiveServerless(name="gpu_worker") @@ -50,7 +50,7 @@ def process(data): # Change to project directory for detection with patch( - "tetra_rp.cli.commands.build_utils.manifest.Path.cwd", + "runpod_flash.cli.commands.build_utils.manifest.Path.cwd", return_value=project_root, ): builder = ManifestBuilder( @@ -66,7 +66,7 @@ def process(data): assert mothership["main_file"] == "main.py" assert mothership["app_variable"] == "app" assert mothership["resource_type"] == "CpuLiveLoadBalancer" - assert mothership["imageName"] == TETRA_CPU_LB_IMAGE + assert mothership["imageName"] == FLASH_CPU_LB_IMAGE def test_manifest_skips_mothership_without_routes(self): """Test mothership NOT added if main.py has no routes.""" @@ -84,7 +84,7 @@ def test_manifest_skips_mothership_without_routes(self): ) with patch( - "tetra_rp.cli.commands.build_utils.manifest.Path.cwd", + "runpod_flash.cli.commands.build_utils.manifest.Path.cwd", return_value=project_root, ): builder = ManifestBuilder(project_name="test", remote_functions=[]) @@ -99,7 +99,7 @@ def test_manifest_skips_mothership_without_main_py(self): project_root = Path(tmpdir) with patch( - "tetra_rp.cli.commands.build_utils.manifest.Path.cwd", + "runpod_flash.cli.commands.build_utils.manifest.Path.cwd", return_value=project_root, ): builder = ManifestBuilder(project_name="test", remote_functions=[]) @@ -130,8 +130,8 @@ def root(): func_file = project_root / "functions.py" func_file.write_text( """ -from tetra_rp import remote -from tetra_rp import LiveServerless +from runpod_flash import remote +from runpod_flash import LiveServerless mothership_config = LiveServerless(name="mothership") @@ -153,7 +153,7 @@ def process(data): ) with patch( - "tetra_rp.cli.commands.build_utils.manifest.Path.cwd", + "runpod_flash.cli.commands.build_utils.manifest.Path.cwd", return_value=project_root, ): builder = ManifestBuilder( @@ -186,7 +186,7 @@ def root(): ) with patch( - "tetra_rp.cli.commands.build_utils.manifest.Path.cwd", + "runpod_flash.cli.commands.build_utils.manifest.Path.cwd", return_value=project_root, ): builder = ManifestBuilder(project_name="test", remote_functions=[]) @@ -199,7 +199,7 @@ def root(): assert mothership["functions"] == [] assert mothership["is_load_balanced"] is True assert mothership["is_live_resource"] is True - assert mothership["imageName"] == TETRA_CPU_LB_IMAGE + assert mothership["imageName"] == FLASH_CPU_LB_IMAGE assert mothership["workersMin"] == 1 assert mothership["workersMax"] == 3 @@ -225,7 +225,7 @@ def root(): mothership_file = project_root / "mothership.py" mothership_file.write_text( """ -from tetra_rp import CpuLiveLoadBalancer +from runpod_flash import CpuLiveLoadBalancer mothership = CpuLiveLoadBalancer( name="my-api", @@ -236,7 +236,7 @@ def root(): ) with patch( - "tetra_rp.cli.commands.build_utils.manifest.Path.cwd", + "runpod_flash.cli.commands.build_utils.manifest.Path.cwd", return_value=project_root, ): builder = ManifestBuilder(project_name="test", remote_functions=[]) @@ -271,7 +271,7 @@ def root(): mothership_file = project_root / "mothership.py" mothership_file.write_text( """ -from tetra_rp import CpuLiveLoadBalancer +from runpod_flash import CpuLiveLoadBalancer mothership = CpuLiveLoadBalancer( name="explicit-mothership", @@ -282,7 +282,7 @@ def root(): ) with patch( - "tetra_rp.cli.commands.build_utils.manifest.Path.cwd", + "runpod_flash.cli.commands.build_utils.manifest.Path.cwd", return_value=project_root, ): builder = ManifestBuilder(project_name="test", remote_functions=[]) @@ -304,7 +304,7 @@ def test_manifest_handles_explicit_mothership_name_conflict(self): mothership_file = project_root / "mothership.py" mothership_file.write_text( """ -from tetra_rp import CpuLiveLoadBalancer +from runpod_flash import CpuLiveLoadBalancer mothership = CpuLiveLoadBalancer( name="api", # Will conflict with @remote resource named "api" @@ -318,8 +318,8 @@ def test_manifest_handles_explicit_mothership_name_conflict(self): func_file = project_root / "functions.py" func_file.write_text( """ -from tetra_rp import remote -from tetra_rp import LiveServerless +from runpod_flash import remote +from runpod_flash import LiveServerless api_config = LiveServerless(name="api") @@ -340,7 +340,7 @@ def process(data): ) with patch( - "tetra_rp.cli.commands.build_utils.manifest.Path.cwd", + "runpod_flash.cli.commands.build_utils.manifest.Path.cwd", return_value=project_root, ): builder = ManifestBuilder( @@ -364,7 +364,7 @@ def test_manifest_explicit_mothership_with_gpu_load_balancer(self): mothership_file = project_root / "mothership.py" mothership_file.write_text( """ -from tetra_rp import LiveLoadBalancer +from runpod_flash import LiveLoadBalancer mothership = LiveLoadBalancer( name="gpu-mothership", @@ -388,7 +388,7 @@ def root(): ) with patch( - "tetra_rp.cli.commands.build_utils.manifest.Path.cwd", + "runpod_flash.cli.commands.build_utils.manifest.Path.cwd", return_value=project_root, ): builder = ManifestBuilder(project_name="test", remote_functions=[]) @@ -396,5 +396,5 @@ def root(): mothership = manifest["resources"]["gpu-mothership"] assert mothership["resource_type"] == "LiveLoadBalancer" - assert mothership["imageName"] == TETRA_LB_IMAGE + assert mothership["imageName"] == FLASH_LB_IMAGE assert mothership["is_explicit"] is True diff --git a/tests/unit/cli/commands/build_utils/test_mothership_handler_generator.py b/tests/unit/cli/commands/build_utils/test_mothership_handler_generator.py new file mode 100644 index 00000000..0a4f6590 --- /dev/null +++ b/tests/unit/cli/commands/build_utils/test_mothership_handler_generator.py @@ -0,0 +1,152 @@ +"""Tests for mothership handler generation.""" + +import tempfile +from pathlib import Path + +import pytest + +from runpod_flash.cli.commands.build_utils.mothership_handler_generator import ( + generate_mothership_handler, +) + + +class TestMothershipeHandlerGenerator: + """Test mothership handler generation.""" + + def test_generate_mothership_handler(self): + """Test basic mothership handler generation.""" + with tempfile.TemporaryDirectory() as tmpdir: + output_path = Path(tmpdir) / "handler_mothership.py" + + generate_mothership_handler( + main_file="main.py", + app_variable="app", + output_path=output_path, + ) + + assert output_path.exists() + content = output_path.read_text() + + # Check expected imports and structure + assert "from main import app" in content + assert "CORSMiddleware" in content + assert "add_middleware(" in content + assert '@app.get("/ping")' in content + assert "async def ping():" in content + assert '"status": "healthy"' in content + assert "MOTHERSHIP_ID = os.getenv(" in content + assert "MOTHERSHIP_URL = os.getenv(" in content + + def test_generate_mothership_handler_custom_app_name(self): + """Test handler generation with custom app variable name.""" + with tempfile.TemporaryDirectory() as tmpdir: + output_path = Path(tmpdir) / "handler_mothership.py" + + generate_mothership_handler( + main_file="server.py", + app_variable="api", + output_path=output_path, + ) + + content = output_path.read_text() + + # Note: Handler always imports as 'app' for convenience + assert "from server import api as app" in content + assert "@app.get" in content + + def test_generate_mothership_handler_creates_directory(self): + """Test handler generator creates output directory if needed.""" + with tempfile.TemporaryDirectory() as tmpdir: + output_path = Path(tmpdir) / "subdir" / "handler_mothership.py" + + generate_mothership_handler( + main_file="main.py", + app_variable="app", + output_path=output_path, + ) + + assert output_path.parent.exists() + assert output_path.exists() + + def test_generate_mothership_handler_invalid_main_file(self): + """Test handler generation fails with invalid main_file.""" + with tempfile.TemporaryDirectory() as tmpdir: + output_path = Path(tmpdir) / "handler_mothership.py" + + with pytest.raises(ValueError, match="Invalid main_file"): + generate_mothership_handler( + main_file="main", # Missing .py extension + app_variable="app", + output_path=output_path, + ) + + def test_generate_mothership_handler_invalid_app_variable(self): + """Test handler generation fails with invalid app_variable.""" + with tempfile.TemporaryDirectory() as tmpdir: + output_path = Path(tmpdir) / "handler_mothership.py" + + with pytest.raises(ValueError, match="Invalid app_variable"): + generate_mothership_handler( + main_file="main.py", + app_variable="123invalid", # Invalid identifier + output_path=output_path, + ) + + def test_generate_mothership_handler_content_structure(self): + """Test generated handler has correct structure.""" + with tempfile.TemporaryDirectory() as tmpdir: + output_path = Path(tmpdir) / "handler_mothership.py" + + generate_mothership_handler( + main_file="main.py", + app_variable="app", + output_path=output_path, + ) + + content = output_path.read_text() + + # Should have docstring + assert "Auto-generated handler for mothership endpoint" in content + # Should have imports + assert "import os" in content + assert "from fastapi.middleware.cors" in content + # Should have CORS setup + assert "CORSMiddleware" in content + # Should have health check + assert "@app.get" in content + + def test_generate_mothership_handler_with_app_py(self): + """Test handler generation with app.py instead of main.py.""" + with tempfile.TemporaryDirectory() as tmpdir: + output_path = Path(tmpdir) / "handler_mothership.py" + + generate_mothership_handler( + main_file="app.py", + app_variable="app", + output_path=output_path, + ) + + content = output_path.read_text() + + assert "from app import app" in content + + def test_generate_mothership_handler_overwrite_existing(self): + """Test handler generation overwrites existing file.""" + with tempfile.TemporaryDirectory() as tmpdir: + output_path = Path(tmpdir) / "handler_mothership.py" + + # Create initial file + output_path.write_text("old content") + + # Generate new handler + generate_mothership_handler( + main_file="main.py", + app_variable="app", + output_path=output_path, + ) + + content = output_path.read_text() + + # Should have new content + assert "old content" not in content + assert "Auto-generated handler" in content diff --git a/tests/unit/cli/commands/build_utils/test_scanner.py b/tests/unit/cli/commands/build_utils/test_scanner.py index cb7cdb49..8f937ecc 100644 --- a/tests/unit/cli/commands/build_utils/test_scanner.py +++ b/tests/unit/cli/commands/build_utils/test_scanner.py @@ -4,7 +4,7 @@ from pathlib import Path -from tetra_rp.cli.commands.build_utils.scanner import RemoteDecoratorScanner +from runpod_flash.cli.commands.build_utils.scanner import RemoteDecoratorScanner def test_discover_simple_function(): @@ -16,7 +16,7 @@ def test_discover_simple_function(): test_file = project_dir / "test_module.py" test_file.write_text( """ -from tetra_rp import LiveServerless, remote +from runpod_flash import LiveServerless, remote gpu_config = LiveServerless(name="test_gpu") @@ -44,7 +44,7 @@ def test_discover_class(): test_file = project_dir / "test_module.py" test_file.write_text( """ -from tetra_rp import LiveServerless, remote +from runpod_flash import LiveServerless, remote gpu_config = LiveServerless(name="test_gpu") @@ -74,7 +74,7 @@ def test_discover_multiple_functions_same_config(): test_file = project_dir / "test_module.py" test_file.write_text( """ -from tetra_rp import LiveServerless, remote +from runpod_flash import LiveServerless, remote gpu_config = LiveServerless(name="gpu_worker") @@ -104,7 +104,7 @@ def test_discover_functions_different_configs(): test_file = project_dir / "test_module.py" test_file.write_text( """ -from tetra_rp import LiveServerless, CpuLiveServerless, remote +from runpod_flash import LiveServerless, CpuLiveServerless, remote gpu_config = LiveServerless(name="gpu_worker") cpu_config = CpuLiveServerless(name="cpu_worker") @@ -139,7 +139,7 @@ def test_discover_nested_module(): test_file = workers_dir / "inference.py" test_file.write_text( """ -from tetra_rp import LiveServerless, remote +from runpod_flash import LiveServerless, remote config = LiveServerless(name="gpu_inference") @@ -165,7 +165,7 @@ def test_discover_inline_config(): test_file = project_dir / "test_module.py" test_file.write_text( """ -from tetra_rp import LiveServerless, remote +from runpod_flash import LiveServerless, remote @remote(LiveServerless(name="inline_config")) async def my_function(data): @@ -210,7 +210,7 @@ def test_discover_sync_function(): test_file = project_dir / "test_module.py" test_file.write_text( """ -from tetra_rp import LiveServerless, remote +from runpod_flash import LiveServerless, remote config = LiveServerless(name="cpu_sync") @@ -238,7 +238,7 @@ def test_exclude_venv_directory(): venv_file = venv_dir / "test_module.py" venv_file.write_text( """ -from tetra_rp import LiveServerless, remote +from runpod_flash import LiveServerless, remote config = LiveServerless(name="venv_config") @@ -252,7 +252,7 @@ async def venv_function(data): project_file = project_dir / "main.py" project_file.write_text( """ -from tetra_rp import LiveServerless, remote +from runpod_flash import LiveServerless, remote config = LiveServerless(name="project_config") @@ -281,7 +281,7 @@ def test_exclude_flash_directory(): flash_file = flash_dir / "generated.py" flash_file.write_text( """ -from tetra_rp import LiveServerless, remote +from runpod_flash import LiveServerless, remote config = LiveServerless(name="flash_config") @@ -295,7 +295,7 @@ async def flash_function(data): project_file = project_dir / "main.py" project_file.write_text( """ -from tetra_rp import LiveServerless, remote +from runpod_flash import LiveServerless, remote config = LiveServerless(name="project_config") @@ -324,7 +324,7 @@ def test_exclude_runpod_directory(): runpod_file = runpod_dir / "cached.py" runpod_file.write_text( """ -from tetra_rp import LiveServerless, remote +from runpod_flash import LiveServerless, remote config = LiveServerless(name="runpod_config") @@ -338,7 +338,7 @@ async def runpod_function(data): project_file = project_dir / "main.py" project_file.write_text( """ -from tetra_rp import LiveServerless, remote +from runpod_flash import LiveServerless, remote config = LiveServerless(name="project_config") @@ -364,7 +364,7 @@ def test_fallback_to_variable_name_when_name_parameter_missing(): test_file = project_dir / "test_module.py" test_file.write_text( """ -from tetra_rp import LiveServerless, remote +from runpod_flash import LiveServerless, remote gpu_config = LiveServerless() @@ -390,7 +390,7 @@ def test_ignore_non_serverless_classes_with_serverless_in_name(): test_file = project_dir / "test_module.py" test_file.write_text( """ -from tetra_rp import LiveServerless, remote +from runpod_flash import LiveServerless, remote class MyServerlessHelper: def __init__(self): @@ -421,7 +421,7 @@ def test_extract_resource_name_with_special_characters(): test_file = project_dir / "test_module.py" test_file.write_text( """ -from tetra_rp import LiveServerless, remote +from runpod_flash import LiveServerless, remote config = LiveServerless(name="01_gpu-worker.v1") @@ -447,7 +447,7 @@ def test_scanner_extracts_config_variable_names(): test_file.write_text( """ -from tetra_rp import LiveLoadBalancer, remote +from runpod_flash import LiveLoadBalancer, remote gpu_config = LiveLoadBalancer(name="my-endpoint") @@ -473,7 +473,7 @@ def test_cpu_live_load_balancer_flags(): test_file.write_text( """ -from tetra_rp import CpuLiveLoadBalancer, remote +from runpod_flash import CpuLiveLoadBalancer, remote cpu_config = CpuLiveLoadBalancer(name="cpu_worker") diff --git a/tests/unit/cli/commands/build_utils/test_scanner_mothership.py b/tests/unit/cli/commands/build_utils/test_scanner_mothership.py index 6803e8e0..480fa0e1 100644 --- a/tests/unit/cli/commands/build_utils/test_scanner_mothership.py +++ b/tests/unit/cli/commands/build_utils/test_scanner_mothership.py @@ -3,7 +3,7 @@ import tempfile from pathlib import Path -from tetra_rp.cli.commands.build_utils.scanner import ( +from runpod_flash.cli.commands.build_utils.scanner import ( detect_explicit_mothership, detect_main_app, ) @@ -90,7 +90,7 @@ def test_detect_main_app_no_routes(self): main_file.write_text( """ from fastapi import FastAPI -from tetra_rp import remote +from runpod_flash import remote app = FastAPI() @@ -277,7 +277,7 @@ def test_detect_explicit_mothership_with_valid_config(self): mothership_file = project_root / "mothership.py" mothership_file.write_text( """ -from tetra_rp import CpuLiveLoadBalancer +from runpod_flash import CpuLiveLoadBalancer mothership = CpuLiveLoadBalancer( name="mothership", @@ -303,7 +303,7 @@ def test_detect_explicit_mothership_with_custom_name(self): mothership_file = project_root / "mothership.py" mothership_file.write_text( """ -from tetra_rp import CpuLiveLoadBalancer +from runpod_flash import CpuLiveLoadBalancer mothership = CpuLiveLoadBalancer( name="my-api-gateway", @@ -325,7 +325,7 @@ def test_detect_explicit_mothership_with_live_load_balancer(self): mothership_file = project_root / "mothership.py" mothership_file.write_text( """ -from tetra_rp import LiveLoadBalancer +from runpod_flash import LiveLoadBalancer mothership = LiveLoadBalancer( name="gpu-mothership", @@ -357,7 +357,7 @@ def test_detect_explicit_mothership_no_mothership_variable(self): mothership_file = project_root / "mothership.py" mothership_file.write_text( """ -from tetra_rp import CpuLiveLoadBalancer +from runpod_flash import CpuLiveLoadBalancer # No mothership variable defined some_config = CpuLiveLoadBalancer(name="other") @@ -375,7 +375,7 @@ def test_detect_explicit_mothership_syntax_error(self): mothership_file = project_root / "mothership.py" mothership_file.write_text( """ -from tetra_rp import CpuLiveLoadBalancer +from runpod_flash import CpuLiveLoadBalancer mothership = CpuLiveLoadBalancer( name="mothership" @@ -394,7 +394,7 @@ def test_detect_explicit_mothership_defaults(self): mothership_file = project_root / "mothership.py" mothership_file.write_text( """ -from tetra_rp import CpuLiveLoadBalancer +from runpod_flash import CpuLiveLoadBalancer mothership = CpuLiveLoadBalancer() """ @@ -415,7 +415,7 @@ def test_detect_explicit_mothership_with_comments(self): mothership_file.write_text( """ # Mothership configuration -from tetra_rp import CpuLiveLoadBalancer +from runpod_flash import CpuLiveLoadBalancer # Use CPU load balancer for cost efficiency mothership = CpuLiveLoadBalancer( @@ -425,7 +425,7 @@ def test_detect_explicit_mothership_with_comments(self): ) # Could also use GPU: -# from tetra_rp import LiveLoadBalancer +# from runpod_flash import LiveLoadBalancer # mothership = LiveLoadBalancer(name="gpu-mothership") """ ) diff --git a/tests/unit/cli/commands/test_build.py b/tests/unit/cli/commands/test_build.py index 6347451b..10333a60 100644 --- a/tests/unit/cli/commands/test_build.py +++ b/tests/unit/cli/commands/test_build.py @@ -1,6 +1,6 @@ """Unit tests for flash build command.""" -from tetra_rp.cli.commands.build import ( +from runpod_flash.cli.commands.build import ( extract_package_name, should_exclude_package, ) diff --git a/tests/unit/cli/commands/test_init.py b/tests/unit/cli/commands/test_init.py index 0ab75187..81910145 100644 --- a/tests/unit/cli/commands/test_init.py +++ b/tests/unit/cli/commands/test_init.py @@ -4,7 +4,7 @@ import pytest -from tetra_rp.cli.commands.init import init_command +from runpod_flash.cli.commands.init import init_command @pytest.fixture @@ -21,13 +21,13 @@ def mock_context(monkeypatch): mocks["console"].status.return_value.__exit__ = Mock(return_value=False) patches = [ - patch("tetra_rp.cli.commands.init.console", mocks["console"]), + patch("runpod_flash.cli.commands.init.console", mocks["console"]), patch( - "tetra_rp.cli.commands.init.detect_file_conflicts", + "runpod_flash.cli.commands.init.detect_file_conflicts", mocks["detect_conflicts"], ), patch( - "tetra_rp.cli.commands.init.create_project_skeleton", + "runpod_flash.cli.commands.init.create_project_skeleton", mocks["create_skeleton"], ), ] diff --git a/tests/unit/cli/commands/test_preview.py b/tests/unit/cli/commands/test_preview.py index 99651c31..d87c2794 100644 --- a/tests/unit/cli/commands/test_preview.py +++ b/tests/unit/cli/commands/test_preview.py @@ -6,7 +6,7 @@ from unittest.mock import MagicMock, patch -from tetra_rp.cli.commands.preview import ( +from runpod_flash.cli.commands.preview import ( CONTAINER_ARCHIVE_PATH, ContainerInfo, _assign_container_port, @@ -310,8 +310,8 @@ def test_display_with_single_mothership(self): class TestVerifyContainerHealth: """Tests for _verify_container_health function.""" - @patch("tetra_rp.cli.commands.preview.subprocess.run") - @patch("tetra_rp.cli.commands.preview.time.sleep") + @patch("runpod_flash.cli.commands.preview.subprocess.run") + @patch("runpod_flash.cli.commands.preview.time.sleep") def test_container_running_succeeds(self, mock_sleep, mock_run): """Test that running container passes health check.""" # Mock docker inspect to return 'running' status @@ -324,8 +324,8 @@ def test_container_running_succeeds(self, mock_sleep, mock_run): _verify_container_health("abc123", "test_resource") mock_sleep.assert_called_once_with(2) - @patch("tetra_rp.cli.commands.preview.subprocess.run") - @patch("tetra_rp.cli.commands.preview.time.sleep") + @patch("runpod_flash.cli.commands.preview.subprocess.run") + @patch("runpod_flash.cli.commands.preview.time.sleep") def test_container_exited_raises_error(self, mock_sleep, mock_run): """Test that exited container raises error.""" # Mock docker inspect to return 'exited' status @@ -338,8 +338,8 @@ def test_container_exited_raises_error(self, mock_sleep, mock_run): with pytest.raises(Exception, match="failed to start"): _verify_container_health("abc123", "test_resource") - @patch("tetra_rp.cli.commands.preview.subprocess.run") - @patch("tetra_rp.cli.commands.preview.time.sleep") + @patch("runpod_flash.cli.commands.preview.subprocess.run") + @patch("runpod_flash.cli.commands.preview.time.sleep") def test_container_health_check_includes_logs(self, mock_sleep, mock_run): """Test that error message includes container logs.""" error_log = "FileNotFoundError: artifact.tar.gz not found" @@ -360,7 +360,7 @@ class TestStartResourceContainer: def test_archive_path_validation(self, tmp_path): """Test that missing archive raises FileNotFoundError.""" - from tetra_rp.cli.commands.preview import _start_resource_container + from runpod_flash.cli.commands.preview import _start_resource_container build_dir = tmp_path / ".flash" / ".build" build_dir.mkdir(parents=True) @@ -376,11 +376,11 @@ def test_archive_path_validation(self, tmp_path): network="test-network", ) - @patch("tetra_rp.cli.commands.preview.subprocess.run") - @patch("tetra_rp.cli.commands.preview._verify_container_health") + @patch("runpod_flash.cli.commands.preview.subprocess.run") + @patch("runpod_flash.cli.commands.preview._verify_container_health") def test_archive_mount_in_docker_command(self, mock_health, mock_run, tmp_path): """Test that archive is mounted at correct location.""" - from tetra_rp.cli.commands.preview import _start_resource_container + from runpod_flash.cli.commands.preview import _start_resource_container build_dir = tmp_path / ".flash" / ".build" build_dir.mkdir(parents=True) diff --git a/tests/unit/cli/commands/test_resource.py b/tests/unit/cli/commands/test_resource.py index dd587649..e42a546c 100644 --- a/tests/unit/cli/commands/test_resource.py +++ b/tests/unit/cli/commands/test_resource.py @@ -4,7 +4,7 @@ import pytest -from tetra_rp.cli.commands.resource import generate_resource_table, report_command +from runpod_flash.cli.commands.resource import generate_resource_table, report_command @pytest.fixture @@ -294,8 +294,8 @@ def test_various_resource_types_no_error(self, mock_resource_manager): pytest.fail(f"generate_resource_table raised {type(e).__name__}: {e}") -@patch("tetra_rp.cli.commands.resource.ResourceManager") -@patch("tetra_rp.cli.commands.resource.console") +@patch("runpod_flash.cli.commands.resource.ResourceManager") +@patch("runpod_flash.cli.commands.resource.console") def test_report_command_static_mode(mock_console, mock_resource_manager_class): """Test report_command in static (non-live) mode.""" mock_manager_instance = MagicMock() @@ -311,10 +311,10 @@ def test_report_command_static_mode(mock_console, mock_resource_manager_class): mock_console.print.assert_called_once() -@patch("tetra_rp.cli.commands.resource.time") -@patch("tetra_rp.cli.commands.resource.Live") -@patch("tetra_rp.cli.commands.resource.ResourceManager") -@patch("tetra_rp.cli.commands.resource.console") +@patch("runpod_flash.cli.commands.resource.time") +@patch("runpod_flash.cli.commands.resource.Live") +@patch("runpod_flash.cli.commands.resource.ResourceManager") +@patch("runpod_flash.cli.commands.resource.console") def test_report_command_live_mode( mock_console, mock_resource_manager_class, mock_live_class, mock_time ): @@ -346,8 +346,8 @@ def sleep_side_effect(duration): assert any("stopped" in str(c).lower() for c in mock_console.print.call_args_list) -@patch("tetra_rp.cli.commands.resource.ResourceManager") -@patch("tetra_rp.cli.commands.resource.console") +@patch("runpod_flash.cli.commands.resource.ResourceManager") +@patch("runpod_flash.cli.commands.resource.console") def test_report_command_with_custom_refresh(mock_console, mock_resource_manager_class): """Test report_command accepts custom refresh interval.""" mock_manager_instance = MagicMock() @@ -361,8 +361,8 @@ def test_report_command_with_custom_refresh(mock_console, mock_resource_manager_ mock_console.print.assert_called_once() -@patch("tetra_rp.cli.commands.resource.ResourceManager") -@patch("tetra_rp.cli.commands.resource.console") +@patch("runpod_flash.cli.commands.resource.ResourceManager") +@patch("runpod_flash.cli.commands.resource.console") def test_report_command_instantiates_resource_manager( mock_console, mock_resource_manager_class ): @@ -377,9 +377,9 @@ def test_report_command_instantiates_resource_manager( mock_resource_manager_class.assert_called_once_with() -@patch("tetra_rp.cli.commands.resource.generate_resource_table") -@patch("tetra_rp.cli.commands.resource.ResourceManager") -@patch("tetra_rp.cli.commands.resource.console") +@patch("runpod_flash.cli.commands.resource.generate_resource_table") +@patch("runpod_flash.cli.commands.resource.ResourceManager") +@patch("runpod_flash.cli.commands.resource.console") def test_report_command_calls_generate_table( mock_console, mock_resource_manager_class, mock_generate_table ): diff --git a/tests/unit/cli/test_apps.py b/tests/unit/cli/test_apps.py index c9d95b42..32328ef4 100644 --- a/tests/unit/cli/test_apps.py +++ b/tests/unit/cli/test_apps.py @@ -7,7 +7,7 @@ from rich.table import Table from typer.testing import CliRunner -from tetra_rp.cli.main import app +from runpod_flash.cli.main import app @pytest.fixture @@ -17,7 +17,7 @@ def runner(): @pytest.fixture def patched_console(): - with patch("tetra_rp.cli.commands.apps.console") as mock_console: + with patch("runpod_flash.cli.commands.apps.console") as mock_console: status_cm = MagicMock() status_cm.__enter__.return_value = None status_cm.__exit__.return_value = None @@ -33,7 +33,7 @@ def test_no_subcommand_shows_help(self, runner): class TestAppsCreate: - @patch("tetra_rp.cli.commands.apps.FlashApp.create", new_callable=AsyncMock) + @patch("runpod_flash.cli.commands.apps.FlashApp.create", new_callable=AsyncMock) def test_create_app_success( self, mock_create, runner, mock_asyncio_run_coro, patched_console ): @@ -42,7 +42,8 @@ def test_create_app_success( mock_create.return_value = created with patch( - "tetra_rp.cli.commands.apps.asyncio.run", side_effect=mock_asyncio_run_coro + "runpod_flash.cli.commands.apps.asyncio.run", + side_effect=mock_asyncio_run_coro, ): result = runner.invoke(app, ["app", "create", "demo-app"]) @@ -54,14 +55,15 @@ def test_create_app_success( assert "demo-app" in panel.renderable assert panel.title == "✅ App Created" - @patch("tetra_rp.cli.commands.apps.FlashApp.create", new_callable=AsyncMock) + @patch("runpod_flash.cli.commands.apps.FlashApp.create", new_callable=AsyncMock) def test_create_app_failure_bubbles_error( self, mock_create, runner, mock_asyncio_run_coro ): mock_create.side_effect = RuntimeError("boom") with patch( - "tetra_rp.cli.commands.apps.asyncio.run", side_effect=mock_asyncio_run_coro + "runpod_flash.cli.commands.apps.asyncio.run", + side_effect=mock_asyncio_run_coro, ): result = runner.invoke(app, ["app", "create", "demo-app"]) @@ -70,21 +72,22 @@ def test_create_app_failure_bubbles_error( class TestAppsList: - @patch("tetra_rp.cli.commands.apps.FlashApp.list", new_callable=AsyncMock) + @patch("runpod_flash.cli.commands.apps.FlashApp.list", new_callable=AsyncMock) def test_list_apps_empty( self, mock_list, runner, mock_asyncio_run_coro, patched_console ): mock_list.return_value = [] with patch( - "tetra_rp.cli.commands.apps.asyncio.run", side_effect=mock_asyncio_run_coro + "runpod_flash.cli.commands.apps.asyncio.run", + side_effect=mock_asyncio_run_coro, ): result = runner.invoke(app, ["app", "list"]) assert result.exit_code == 0 patched_console.print.assert_called_with("No Flash apps found.") - @patch("tetra_rp.cli.commands.apps.FlashApp.list", new_callable=AsyncMock) + @patch("runpod_flash.cli.commands.apps.FlashApp.list", new_callable=AsyncMock) def test_list_apps_with_data( self, mock_list, runner, mock_asyncio_run_coro, patched_console ): @@ -120,7 +123,8 @@ def test_list_apps_with_data( ] with patch( - "tetra_rp.cli.commands.apps.asyncio.run", side_effect=mock_asyncio_run_coro + "runpod_flash.cli.commands.apps.asyncio.run", + side_effect=mock_asyncio_run_coro, ): result = runner.invoke(app, ["app", "list"]) @@ -132,14 +136,15 @@ def test_list_apps_with_data( assert "dev, prod" in columns[2]._cells[0] assert "build-1" in columns[3]._cells[0] - @patch("tetra_rp.cli.commands.apps.FlashApp.list", new_callable=AsyncMock) + @patch("runpod_flash.cli.commands.apps.FlashApp.list", new_callable=AsyncMock) def test_list_and_ls_share_logic( self, mock_list, runner, mock_asyncio_run_coro, patched_console ): mock_list.return_value = [] with patch( - "tetra_rp.cli.commands.apps.asyncio.run", side_effect=mock_asyncio_run_coro + "runpod_flash.cli.commands.apps.asyncio.run", + side_effect=mock_asyncio_run_coro, ): result_ls = runner.invoke(app, ["app", "ls"]) result_list = runner.invoke(app, ["app", "list"]) @@ -150,7 +155,7 @@ def test_list_and_ls_share_logic( class TestAppsGet: - @patch("tetra_rp.cli.commands.apps.FlashApp.from_name", new_callable=AsyncMock) + @patch("runpod_flash.cli.commands.apps.FlashApp.from_name", new_callable=AsyncMock) def test_get_app_details( self, mock_from_name, runner, mock_asyncio_run_coro, patched_console ): @@ -174,7 +179,8 @@ def test_get_app_details( mock_from_name.return_value = flash_app with patch( - "tetra_rp.cli.commands.apps.asyncio.run", side_effect=mock_asyncio_run_coro + "runpod_flash.cli.commands.apps.asyncio.run", + side_effect=mock_asyncio_run_coro, ): result = runner.invoke(app, ["app", "get", "demo"]) @@ -190,7 +196,7 @@ def test_get_app_details( assert isinstance(env_table, Table) assert isinstance(build_table, Table) - @patch("tetra_rp.cli.commands.apps.FlashApp.from_name", new_callable=AsyncMock) + @patch("runpod_flash.cli.commands.apps.FlashApp.from_name", new_callable=AsyncMock) def test_get_app_without_related_data( self, mock_from_name, runner, mock_asyncio_run_coro, patched_console ): @@ -202,7 +208,8 @@ def test_get_app_without_related_data( mock_from_name.return_value = flash_app with patch( - "tetra_rp.cli.commands.apps.asyncio.run", side_effect=mock_asyncio_run_coro + "runpod_flash.cli.commands.apps.asyncio.run", + side_effect=mock_asyncio_run_coro, ): result = runner.invoke(app, ["app", "get", "demo"]) @@ -213,14 +220,15 @@ def test_get_app_without_related_data( class TestAppsDelete: - @patch("tetra_rp.cli.commands.apps.FlashApp.delete", new_callable=AsyncMock) + @patch("runpod_flash.cli.commands.apps.FlashApp.delete", new_callable=AsyncMock) def test_delete_app_success( self, mock_delete, runner, mock_asyncio_run_coro, patched_console ): mock_delete.return_value = True with patch( - "tetra_rp.cli.commands.apps.asyncio.run", side_effect=mock_asyncio_run_coro + "runpod_flash.cli.commands.apps.asyncio.run", + side_effect=mock_asyncio_run_coro, ): result = runner.invoke(app, ["app", "delete", "--app-name", "demo"]) @@ -230,22 +238,23 @@ def test_delete_app_success( "✅ Flash app 'demo' deleted successfully" ) - @patch("tetra_rp.cli.commands.apps.FlashApp.delete", new_callable=AsyncMock) + @patch("runpod_flash.cli.commands.apps.FlashApp.delete", new_callable=AsyncMock) def test_delete_app_failure_raises_exit( self, mock_delete, runner, mock_asyncio_run_coro, patched_console ): mock_delete.return_value = False with patch( - "tetra_rp.cli.commands.apps.asyncio.run", side_effect=mock_asyncio_run_coro + "runpod_flash.cli.commands.apps.asyncio.run", + side_effect=mock_asyncio_run_coro, ): result = runner.invoke(app, ["app", "delete", "--app-name", "demo"]) assert result.exit_code == 1 patched_console.print.assert_called_with("❌ Failed to delete flash app 'demo'") - @patch("tetra_rp.cli.commands.apps.discover_flash_project") - @patch("tetra_rp.cli.commands.apps.FlashApp.delete", new_callable=AsyncMock) + @patch("runpod_flash.cli.commands.apps.discover_flash_project") + @patch("runpod_flash.cli.commands.apps.FlashApp.delete", new_callable=AsyncMock) def test_delete_app_uses_discovered_name( self, mock_delete, @@ -258,7 +267,8 @@ def test_delete_app_uses_discovered_name( mock_discover.return_value = ("/tmp/flash", "derived") with patch( - "tetra_rp.cli.commands.apps.asyncio.run", side_effect=mock_asyncio_run_coro + "runpod_flash.cli.commands.apps.asyncio.run", + side_effect=mock_asyncio_run_coro, ): result = runner.invoke(app, ["app", "delete", "--app-name", ""]) diff --git a/tests/unit/cli/test_deploy.py b/tests/unit/cli/test_deploy.py index 27a166ba..6d8e8b5b 100644 --- a/tests/unit/cli/test_deploy.py +++ b/tests/unit/cli/test_deploy.py @@ -7,7 +7,7 @@ from rich.table import Table from typer.testing import CliRunner -from tetra_rp.cli.main import app +from runpod_flash.cli.main import app @pytest.fixture @@ -17,7 +17,7 @@ def runner(): @pytest.fixture def patched_console(): - with patch("tetra_rp.cli.commands.deploy.console") as mock_console: + with patch("runpod_flash.cli.commands.deploy.console") as mock_console: status_cm = MagicMock() status_cm.__enter__.return_value = None status_cm.__exit__.return_value = None @@ -26,7 +26,9 @@ def patched_console(): class TestDeployList: - @patch("tetra_rp.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock) + @patch( + "runpod_flash.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock + ) def test_list_environments_empty( self, mock_from_name, runner, mock_asyncio_run_coro, patched_console ): @@ -35,7 +37,7 @@ def test_list_environments_empty( mock_from_name.return_value = flash_app with patch( - "tetra_rp.cli.commands.deploy.asyncio.run", + "runpod_flash.cli.commands.deploy.asyncio.run", side_effect=mock_asyncio_run_coro, ): result = runner.invoke(app, ["deploy", "list", "--app-name", "demo"]) @@ -44,7 +46,9 @@ def test_list_environments_empty( patched_console.print.assert_called_with("No environments found for 'demo'.") mock_from_name.assert_awaited_once_with("demo") - @patch("tetra_rp.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock) + @patch( + "runpod_flash.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock + ) def test_list_environments_with_data( self, mock_from_name, runner, mock_asyncio_run_coro, patched_console ): @@ -62,7 +66,7 @@ def test_list_environments_with_data( mock_from_name.return_value = flash_app with patch( - "tetra_rp.cli.commands.deploy.asyncio.run", + "runpod_flash.cli.commands.deploy.asyncio.run", side_effect=mock_asyncio_run_coro, ): result = runner.invoke(app, ["deploy", "list", "--app-name", "demo"]) @@ -73,8 +77,10 @@ def test_list_environments_with_data( assert table.columns[0]._cells[0] == "dev" assert table.columns[2]._cells[0] == "build-1" - @patch("tetra_rp.cli.commands.deploy.discover_flash_project") - @patch("tetra_rp.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock) + @patch("runpod_flash.cli.commands.deploy.discover_flash_project") + @patch( + "runpod_flash.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock + ) def test_list_envs_uses_discovery( self, mock_from_name, @@ -89,7 +95,7 @@ def test_list_envs_uses_discovery( mock_from_name.return_value = flash_app with patch( - "tetra_rp.cli.commands.deploy.asyncio.run", + "runpod_flash.cli.commands.deploy.asyncio.run", side_effect=mock_asyncio_run_coro, ): result = runner.invoke(app, ["deploy", "list"]) @@ -101,7 +107,7 @@ def test_list_envs_uses_discovery( class TestDeployNew: @patch( - "tetra_rp.cli.commands.deploy.FlashApp.create_environment_and_app", + "runpod_flash.cli.commands.deploy.FlashApp.create_environment_and_app", new_callable=AsyncMock, ) def test_new_environment_success( @@ -118,7 +124,7 @@ def test_new_environment_success( mock_create.return_value = (mock_app, mock_env) with patch( - "tetra_rp.cli.commands.deploy.asyncio.run", + "runpod_flash.cli.commands.deploy.asyncio.run", side_effect=mock_asyncio_run_coro, ): result = runner.invoke(app, ["deploy", "new", "dev", "--app-name", "demo"]) @@ -133,7 +139,9 @@ def test_new_environment_success( class TestDeployInfo: - @patch("tetra_rp.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock) + @patch( + "runpod_flash.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock + ) def test_info_includes_children( self, mock_from_name, runner, mock_asyncio_run_coro, patched_console ): @@ -152,7 +160,7 @@ def test_info_includes_children( mock_from_name.return_value = flash_app with patch( - "tetra_rp.cli.commands.deploy.asyncio.run", + "runpod_flash.cli.commands.deploy.asyncio.run", side_effect=mock_asyncio_run_coro, ): result = runner.invoke(app, ["deploy", "info", "dev", "--app-name", "demo"]) @@ -165,7 +173,9 @@ def test_info_includes_children( assert isinstance(endpoint_table, Table) assert isinstance(network_table, Table) - @patch("tetra_rp.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock) + @patch( + "runpod_flash.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock + ) def test_info_without_children( self, mock_from_name, runner, mock_asyncio_run_coro, patched_console ): @@ -184,7 +194,7 @@ def test_info_without_children( mock_from_name.return_value = flash_app with patch( - "tetra_rp.cli.commands.deploy.asyncio.run", + "runpod_flash.cli.commands.deploy.asyncio.run", side_effect=mock_asyncio_run_coro, ): result = runner.invoke(app, ["deploy", "info", "dev", "--app-name", "demo"]) @@ -197,10 +207,13 @@ def test_info_without_children( class TestDeployDelete: @patch( - "tetra_rp.cli.commands.deploy._fetch_environment_info", new_callable=AsyncMock + "runpod_flash.cli.commands.deploy._fetch_environment_info", + new_callable=AsyncMock, + ) + @patch("runpod_flash.cli.commands.deploy.questionary") + @patch( + "runpod_flash.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock ) - @patch("tetra_rp.cli.commands.deploy.questionary") - @patch("tetra_rp.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock) def test_delete_environment_success( self, mock_from_name, @@ -219,13 +232,7 @@ def test_delete_environment_success( flash_app = MagicMock() flash_app.get_environment_by_name = AsyncMock( - return_value={ - "id": "env-1", - "name": "dev", - "activeBuildId": "build-1", - "endpoints": [], - "networkVolumes": [], - } + return_value=mock_fetch_env.return_value ) flash_app.delete_environment = AsyncMock(return_value=True) mock_from_name.return_value = flash_app @@ -235,7 +242,7 @@ def test_delete_environment_success( mock_questionary.confirm.return_value = confirm with patch( - "tetra_rp.cli.commands.deploy.asyncio.run", + "runpod_flash.cli.commands.deploy.asyncio.run", side_effect=mock_asyncio_run_coro, ): result = runner.invoke( @@ -254,176 +261,13 @@ def test_delete_environment_success( ) @patch( - "tetra_rp.cli.commands.deploy._fetch_environment_info", new_callable=AsyncMock - ) - @patch("tetra_rp.cli.commands.deploy._get_resource_manager") - @patch("tetra_rp.cli.commands.deploy.questionary") - @patch("tetra_rp.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock) - def test_delete_environment_undeploys_resources( - self, - mock_from_name, - mock_questionary, - mock_get_manager, - mock_fetch_env, - runner, - mock_asyncio_run_coro, - ): - flash_app = MagicMock() - flash_app.get_environment_by_name = AsyncMock( - return_value={ - "id": "env-1", - "endpoints": [{"id": "endpoint-1", "name": "api"}], - "networkVolumes": [], - } - ) - flash_app.delete_environment = AsyncMock(return_value=True) - mock_from_name.return_value = flash_app - - confirm = MagicMock() - confirm.ask.return_value = True - mock_questionary.confirm.return_value = confirm - - mock_fetch_env.return_value = { - "id": "env-1", - "name": "dev", - "activeBuildId": None, - } - - resource = MagicMock() - resource.name = "api" - manager = MagicMock() - manager.find_resources_by_provider_id.return_value = [("resource-1", resource)] - manager.undeploy_resource = AsyncMock(return_value={"success": True}) - mock_get_manager.return_value = manager - - with patch( - "tetra_rp.cli.commands.deploy.asyncio.run", - side_effect=mock_asyncio_run_coro, - ): - result = runner.invoke( - app, ["deploy", "delete", "dev", "--app-name", "demo"] - ) - - assert result.exit_code == 0 - manager.find_resources_by_provider_id.assert_called_once_with("endpoint-1") - manager.undeploy_resource.assert_awaited_once_with("resource-1", "api") - flash_app.delete_environment.assert_awaited_once_with("dev") - - @patch( - "tetra_rp.cli.commands.deploy._fetch_environment_info", new_callable=AsyncMock - ) - @patch("tetra_rp.cli.commands.deploy._get_resource_manager") - @patch("tetra_rp.cli.commands.deploy.questionary") - @patch("tetra_rp.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock) - def test_delete_environment_aborts_on_undeploy_failure( - self, - mock_from_name, - mock_questionary, - mock_get_manager, - mock_fetch_env, - runner, - mock_asyncio_run_coro, - ): - flash_app = MagicMock() - flash_app.get_environment_by_name = AsyncMock( - return_value={ - "id": "env-1", - "endpoints": [{"id": "endpoint-1", "name": "api"}], - "networkVolumes": [], - } - ) - flash_app.delete_environment = AsyncMock(return_value=True) - mock_from_name.return_value = flash_app - - confirm = MagicMock() - confirm.ask.return_value = True - mock_questionary.confirm.return_value = confirm - - mock_fetch_env.return_value = { - "id": "env-1", - "name": "dev", - "activeBuildId": None, - } - - resource = MagicMock() - resource.name = "api" - manager = MagicMock() - manager.find_resources_by_provider_id.return_value = [("resource-1", resource)] - manager.undeploy_resource = AsyncMock( - return_value={"success": False, "message": "undeploy failed"} - ) - mock_get_manager.return_value = manager - - with patch( - "tetra_rp.cli.commands.deploy.asyncio.run", - side_effect=mock_asyncio_run_coro, - ): - result = runner.invoke( - app, ["deploy", "delete", "dev", "--app-name", "demo"] - ) - - assert result.exit_code == 1 - manager.find_resources_by_provider_id.assert_called_once_with("endpoint-1") - manager.undeploy_resource.assert_awaited_once_with("resource-1", "api") - flash_app.delete_environment.assert_not_called() - - @patch( - "tetra_rp.cli.commands.deploy._fetch_environment_info", new_callable=AsyncMock + "runpod_flash.cli.commands.deploy._fetch_environment_info", + new_callable=AsyncMock, ) - @patch("tetra_rp.cli.commands.deploy._get_resource_manager") - @patch("tetra_rp.cli.commands.deploy.questionary") - @patch("tetra_rp.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock) - def test_delete_environment_aborts_when_endpoint_id_missing( - self, - mock_from_name, - mock_questionary, - mock_get_manager, - mock_fetch_env, - runner, - mock_asyncio_run_coro, - ): - flash_app = MagicMock() - flash_app.get_environment_by_name = AsyncMock( - return_value={ - "id": "env-1", - "endpoints": [{"name": "api"}], - "networkVolumes": [], - } - ) - flash_app.delete_environment = AsyncMock(return_value=True) - mock_from_name.return_value = flash_app - - confirm = MagicMock() - confirm.ask.return_value = True - mock_questionary.confirm.return_value = confirm - - mock_fetch_env.return_value = { - "id": "env-1", - "name": "dev", - "activeBuildId": None, - } - - manager = MagicMock() - mock_get_manager.return_value = manager - - with patch( - "tetra_rp.cli.commands.deploy.asyncio.run", - side_effect=mock_asyncio_run_coro, - ): - result = runner.invoke( - app, ["deploy", "delete", "dev", "--app-name", "demo"] - ) - - assert result.exit_code == 1 - manager.find_resources_by_provider_id.assert_not_called() - manager.undeploy_resource.assert_not_called() - flash_app.delete_environment.assert_not_called() - + @patch("runpod_flash.cli.commands.deploy.questionary") @patch( - "tetra_rp.cli.commands.deploy._fetch_environment_info", new_callable=AsyncMock + "runpod_flash.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock ) - @patch("tetra_rp.cli.commands.deploy.questionary") - @patch("tetra_rp.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock) def test_delete_environment_cancelled( self, mock_from_name, @@ -447,7 +291,7 @@ def test_delete_environment_cancelled( mock_questionary.confirm.return_value = confirm with patch( - "tetra_rp.cli.commands.deploy.asyncio.run", + "runpod_flash.cli.commands.deploy.asyncio.run", side_effect=mock_asyncio_run_coro, ): result = runner.invoke( @@ -461,19 +305,17 @@ def test_delete_environment_cancelled( patched_console.print.assert_any_call("Deletion cancelled") @patch( - "tetra_rp.cli.commands.deploy._fetch_environment_info", new_callable=AsyncMock + "runpod_flash.cli.commands.deploy._fetch_environment_info", + new_callable=AsyncMock, ) + @patch("runpod_flash.cli.commands.deploy.questionary") @patch( - "tetra_rp.cli.commands.deploy._undeploy_environment_resources", - new_callable=AsyncMock, + "runpod_flash.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock ) - @patch("tetra_rp.cli.commands.deploy.questionary") - @patch("tetra_rp.cli.commands.deploy.FlashApp.from_name", new_callable=AsyncMock) def test_delete_environment_failure( self, mock_from_name, mock_questionary, - mock_undeploy_resources, mock_fetch_env, runner, mock_asyncio_run_coro, @@ -487,13 +329,8 @@ def test_delete_environment_failure( flash_app = MagicMock() flash_app.get_environment_by_name = AsyncMock( - return_value={ - "id": "env-1", - "endpoints": [{"id": "endpoint-1", "name": "api"}], - "networkVolumes": [], - } + return_value=mock_fetch_env.return_value ) - flash_app.delete_environment = AsyncMock(return_value=False) mock_from_name.return_value = flash_app @@ -501,10 +338,8 @@ def test_delete_environment_failure( confirm.ask.return_value = True mock_questionary.confirm.return_value = confirm - mock_undeploy_resources.return_value = None - with patch( - "tetra_rp.cli.commands.deploy.asyncio.run", + "runpod_flash.cli.commands.deploy.asyncio.run", side_effect=mock_asyncio_run_coro, ): result = runner.invoke( @@ -512,13 +347,14 @@ def test_delete_environment_failure( ) assert result.exit_code == 1 - mock_undeploy_resources.assert_awaited_once() flash_app.delete_environment.assert_awaited_once_with("dev") patched_console.print.assert_any_call("❌ Failed to delete environment 'dev'") class TestDeploySend: - @patch("tetra_rp.cli.commands.deploy.deploy_to_environment", new_callable=AsyncMock) + @patch( + "runpod_flash.cli.commands.deploy.deploy_to_environment", new_callable=AsyncMock + ) def test_send_success( self, mock_deploy, runner, mock_asyncio_run_coro, patched_console ): @@ -526,9 +362,9 @@ def test_send_success( build_path.exists.return_value = True with ( - patch("tetra_rp.cli.commands.deploy.Path", return_value=build_path), + patch("runpod_flash.cli.commands.deploy.Path", return_value=build_path), patch( - "tetra_rp.cli.commands.deploy.asyncio.run", + "runpod_flash.cli.commands.deploy.asyncio.run", side_effect=mock_asyncio_run_coro, ), ): @@ -547,7 +383,7 @@ def test_send_missing_build_path_errors(self, runner): missing_path = MagicMock() missing_path.exists.return_value = False - with patch("tetra_rp.cli.commands.deploy.Path", return_value=missing_path): + with patch("runpod_flash.cli.commands.deploy.Path", return_value=missing_path): result = runner.invoke( app, ["deploy", "send", "dev", "--app-name", "demo"], diff --git a/tests/unit/cli/test_run.py b/tests/unit/cli/test_run.py index 4e96e28b..a652aa75 100644 --- a/tests/unit/cli/test_run.py +++ b/tests/unit/cli/test_run.py @@ -4,7 +4,7 @@ from unittest.mock import patch, MagicMock from typer.testing import CliRunner -from tetra_rp.cli.main import app +from runpod_flash.cli.main import app @pytest.fixture @@ -32,16 +32,16 @@ def test_port_from_environment_variable( monkeypatch.setenv("FLASH_PORT", "8080") # Mock subprocess to capture command and prevent actual server start - with patch("tetra_rp.cli.commands.run.subprocess.Popen") as mock_popen: + with patch("runpod_flash.cli.commands.run.subprocess.Popen") as mock_popen: mock_process = MagicMock() mock_process.pid = 12345 mock_process.wait.side_effect = KeyboardInterrupt() mock_popen.return_value = mock_process # Mock OS-level process group operations - with patch("tetra_rp.cli.commands.run.os.getpgid") as mock_getpgid: + with patch("runpod_flash.cli.commands.run.os.getpgid") as mock_getpgid: mock_getpgid.return_value = 12345 - with patch("tetra_rp.cli.commands.run.os.killpg"): + with patch("runpod_flash.cli.commands.run.os.killpg"): runner.invoke(app, ["run"]) # Verify port 8080 was used in uvicorn command @@ -58,16 +58,16 @@ def test_host_from_environment_variable( monkeypatch.setenv("FLASH_HOST", "0.0.0.0") # Mock subprocess to capture command - with patch("tetra_rp.cli.commands.run.subprocess.Popen") as mock_popen: + with patch("runpod_flash.cli.commands.run.subprocess.Popen") as mock_popen: mock_process = MagicMock() mock_process.pid = 12345 mock_process.wait.side_effect = KeyboardInterrupt() mock_popen.return_value = mock_process # Mock OS-level operations - with patch("tetra_rp.cli.commands.run.os.getpgid") as mock_getpgid: + with patch("runpod_flash.cli.commands.run.os.getpgid") as mock_getpgid: mock_getpgid.return_value = 12345 - with patch("tetra_rp.cli.commands.run.os.killpg"): + with patch("runpod_flash.cli.commands.run.os.killpg"): runner.invoke(app, ["run"]) # Verify host 0.0.0.0 was used @@ -84,16 +84,16 @@ def test_cli_flag_overrides_environment_variable( monkeypatch.setenv("FLASH_PORT", "8080") # Mock subprocess to capture command - with patch("tetra_rp.cli.commands.run.subprocess.Popen") as mock_popen: + with patch("runpod_flash.cli.commands.run.subprocess.Popen") as mock_popen: mock_process = MagicMock() mock_process.pid = 12345 mock_process.wait.side_effect = KeyboardInterrupt() mock_popen.return_value = mock_process # Mock OS-level operations - with patch("tetra_rp.cli.commands.run.os.getpgid") as mock_getpgid: + with patch("runpod_flash.cli.commands.run.os.getpgid") as mock_getpgid: mock_getpgid.return_value = 12345 - with patch("tetra_rp.cli.commands.run.os.killpg"): + with patch("runpod_flash.cli.commands.run.os.killpg"): # Use --port flag to override env var runner.invoke(app, ["run", "--port", "9000"]) @@ -112,16 +112,16 @@ def test_default_port_when_no_env_or_flag( monkeypatch.delenv("FLASH_PORT", raising=False) # Mock subprocess to capture command - with patch("tetra_rp.cli.commands.run.subprocess.Popen") as mock_popen: + with patch("runpod_flash.cli.commands.run.subprocess.Popen") as mock_popen: mock_process = MagicMock() mock_process.pid = 12345 mock_process.wait.side_effect = KeyboardInterrupt() mock_popen.return_value = mock_process # Mock OS-level operations - with patch("tetra_rp.cli.commands.run.os.getpgid") as mock_getpgid: + with patch("runpod_flash.cli.commands.run.os.getpgid") as mock_getpgid: mock_getpgid.return_value = 12345 - with patch("tetra_rp.cli.commands.run.os.killpg"): + with patch("runpod_flash.cli.commands.run.os.killpg"): runner.invoke(app, ["run"]) # Verify default port 8888 was used @@ -139,16 +139,16 @@ def test_default_host_when_no_env_or_flag( monkeypatch.delenv("FLASH_HOST", raising=False) # Mock subprocess to capture command - with patch("tetra_rp.cli.commands.run.subprocess.Popen") as mock_popen: + with patch("runpod_flash.cli.commands.run.subprocess.Popen") as mock_popen: mock_process = MagicMock() mock_process.pid = 12345 mock_process.wait.side_effect = KeyboardInterrupt() mock_popen.return_value = mock_process # Mock OS-level operations - with patch("tetra_rp.cli.commands.run.os.getpgid") as mock_getpgid: + with patch("runpod_flash.cli.commands.run.os.getpgid") as mock_getpgid: mock_getpgid.return_value = 12345 - with patch("tetra_rp.cli.commands.run.os.killpg"): + with patch("runpod_flash.cli.commands.run.os.killpg"): runner.invoke(app, ["run"]) # Verify default host localhost was used @@ -166,16 +166,16 @@ def test_both_host_and_port_from_environment( monkeypatch.setenv("FLASH_PORT", "3000") # Mock subprocess to capture command - with patch("tetra_rp.cli.commands.run.subprocess.Popen") as mock_popen: + with patch("runpod_flash.cli.commands.run.subprocess.Popen") as mock_popen: mock_process = MagicMock() mock_process.pid = 12345 mock_process.wait.side_effect = KeyboardInterrupt() mock_popen.return_value = mock_process # Mock OS-level operations - with patch("tetra_rp.cli.commands.run.os.getpgid") as mock_getpgid: + with patch("runpod_flash.cli.commands.run.os.getpgid") as mock_getpgid: mock_getpgid.return_value = 12345 - with patch("tetra_rp.cli.commands.run.os.killpg"): + with patch("runpod_flash.cli.commands.run.os.killpg"): runner.invoke(app, ["run"]) # Verify both host and port were used @@ -197,16 +197,16 @@ def test_short_port_flag_overrides_environment( monkeypatch.setenv("FLASH_PORT", "8080") # Mock subprocess to capture command - with patch("tetra_rp.cli.commands.run.subprocess.Popen") as mock_popen: + with patch("runpod_flash.cli.commands.run.subprocess.Popen") as mock_popen: mock_process = MagicMock() mock_process.pid = 12345 mock_process.wait.side_effect = KeyboardInterrupt() mock_popen.return_value = mock_process # Mock OS-level operations - with patch("tetra_rp.cli.commands.run.os.getpgid") as mock_getpgid: + with patch("runpod_flash.cli.commands.run.os.getpgid") as mock_getpgid: mock_getpgid.return_value = 12345 - with patch("tetra_rp.cli.commands.run.os.killpg"): + with patch("runpod_flash.cli.commands.run.os.killpg"): # Use -p short flag runner.invoke(app, ["run", "-p", "7000"]) diff --git a/tests/unit/cli/test_undeploy.py b/tests/unit/cli/test_undeploy.py index bbbb8fb4..16113163 100644 --- a/tests/unit/cli/test_undeploy.py +++ b/tests/unit/cli/test_undeploy.py @@ -4,10 +4,10 @@ from unittest.mock import MagicMock, patch from typer.testing import CliRunner -from tetra_rp.cli.main import app -from tetra_rp.core.resources.serverless import ServerlessResource -from tetra_rp.core.resources.network_volume import NetworkVolume -from tetra_rp.core.resources.resource_manager import ResourceManager +from runpod_flash.cli.main import app +from runpod_flash.core.resources.serverless import ServerlessResource +from runpod_flash.core.resources.network_volume import NetworkVolume +from runpod_flash.core.resources.resource_manager import ResourceManager @pytest.fixture(autouse=True) @@ -63,7 +63,7 @@ class TestUndeployList: def test_list_no_endpoints(self, runner): """Test list command with no endpoints.""" with patch( - "tetra_rp.cli.commands.undeploy._get_resource_manager" + "runpod_flash.cli.commands.undeploy._get_resource_manager" ) as mock_get_rm: mock_manager = MagicMock() mock_manager.list_all_resources.return_value = {} @@ -76,7 +76,7 @@ def test_list_no_endpoints(self, runner): def test_list_with_endpoints(self, runner): """Test list command with endpoints.""" - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.core.resources.serverless import ServerlessResource # Create mock resources that are instances of ServerlessResource mock_resource1 = MagicMock(spec=ServerlessResource) @@ -95,7 +95,7 @@ def test_list_with_endpoints(self, runner): } with patch( - "tetra_rp.cli.commands.undeploy._get_resource_manager" + "runpod_flash.cli.commands.undeploy._get_resource_manager" ) as mock_get_rm: mock_manager = MagicMock() mock_manager.list_all_resources.return_value = mock_resources @@ -114,7 +114,7 @@ def test_list_with_endpoints(self, runner): def test_list_filters_non_serverless_resources(self, runner): """Test that list command filters out non-ServerlessResource types.""" - from tetra_rp.cli.commands.undeploy import _get_serverless_resources + from runpod_flash.cli.commands.undeploy import _get_serverless_resources # Create mock ServerlessResource instances mock_serverless1 = MagicMock(spec=ServerlessResource) @@ -160,7 +160,7 @@ class TestUndeployCommand: def test_undeploy_no_args_shows_help(self, runner): """Test undeploy without arguments shows help/usage.""" with patch( - "tetra_rp.cli.commands.undeploy._get_resource_manager" + "runpod_flash.cli.commands.undeploy._get_resource_manager" ) as mock_get_rm: mock_manager = MagicMock() mock_manager.list_all_resources.return_value = {} @@ -175,7 +175,7 @@ def test_undeploy_no_args_shows_help(self, runner): def test_undeploy_no_args_shows_usage_text(self, runner): """Ensure usage panel is rendered when no args are provided.""" with patch( - "tetra_rp.cli.commands.undeploy._get_resource_manager" + "runpod_flash.cli.commands.undeploy._get_resource_manager" ) as mock_get_rm: mock_resource = MagicMock() mock_resource.name = "foo" @@ -191,7 +191,7 @@ def test_undeploy_no_args_shows_usage_text(self, runner): def test_undeploy_nonexistent_name(self, runner, sample_resources): """Test undeploy with nonexistent name.""" with patch( - "tetra_rp.cli.commands.undeploy._get_resource_manager" + "runpod_flash.cli.commands.undeploy._get_resource_manager" ) as mock_get_rm: mock_manager = MagicMock() mock_manager.list_all_resources.return_value = sample_resources @@ -206,9 +206,9 @@ def test_undeploy_by_name_cancelled(self, runner, sample_resources): """Test undeploy by name cancelled by user.""" with ( patch( - "tetra_rp.cli.commands.undeploy._get_resource_manager" + "runpod_flash.cli.commands.undeploy._get_resource_manager" ) as mock_get_rm, - patch("tetra_rp.cli.commands.undeploy.questionary") as mock_questionary, + patch("runpod_flash.cli.commands.undeploy.questionary") as mock_questionary, ): mock_manager = MagicMock() mock_manager.list_all_resources.return_value = sample_resources @@ -224,16 +224,16 @@ def test_undeploy_by_name_cancelled(self, runner, sample_resources): assert result.exit_code == 0 assert "cancelled" in result.stdout.lower() - @patch("tetra_rp.cli.commands.undeploy.asyncio.run") + @patch("runpod_flash.cli.commands.undeploy.asyncio.run") def test_undeploy_by_name_success( self, mock_asyncio_run, runner, sample_resources, mock_asyncio_run_coro ): """Test successful undeploy by name.""" with ( patch( - "tetra_rp.cli.commands.undeploy._get_resource_manager" + "runpod_flash.cli.commands.undeploy._get_resource_manager" ) as mock_get_rm, - patch("tetra_rp.cli.commands.undeploy.questionary") as mock_questionary, + patch("runpod_flash.cli.commands.undeploy.questionary") as mock_questionary, ): mock_manager = MagicMock() mock_manager.list_all_resources.return_value = sample_resources @@ -262,16 +262,16 @@ async def mock_undeploy(resource_id, name): assert result.exit_code == 0 assert "Successfully" in result.stdout - @patch("tetra_rp.cli.commands.undeploy.asyncio.run") + @patch("runpod_flash.cli.commands.undeploy.asyncio.run") def test_undeploy_all_flag( self, mock_asyncio_run, runner, sample_resources, mock_asyncio_run_coro ): """Test undeploy --all flag.""" with ( patch( - "tetra_rp.cli.commands.undeploy._get_resource_manager" + "runpod_flash.cli.commands.undeploy._get_resource_manager" ) as mock_get_rm, - patch("tetra_rp.cli.commands.undeploy.questionary") as mock_questionary, + patch("runpod_flash.cli.commands.undeploy.questionary") as mock_questionary, ): mock_manager = MagicMock() mock_manager.list_all_resources.return_value = sample_resources @@ -308,9 +308,9 @@ def test_undeploy_all_wrong_confirmation(self, runner, sample_resources): """Test undeploy --all with wrong confirmation text.""" with ( patch( - "tetra_rp.cli.commands.undeploy._get_resource_manager" + "runpod_flash.cli.commands.undeploy._get_resource_manager" ) as mock_get_rm, - patch("tetra_rp.cli.commands.undeploy.questionary") as mock_questionary, + patch("runpod_flash.cli.commands.undeploy.questionary") as mock_questionary, ): mock_manager = MagicMock() mock_manager.list_all_resources.return_value = sample_resources @@ -336,7 +336,7 @@ class TestResourceStatusHelpers: def test_get_resource_status_active(self): """Test _get_resource_status for active resource.""" - from tetra_rp.cli.commands.undeploy import _get_resource_status + from runpod_flash.cli.commands.undeploy import _get_resource_status mock_resource = MagicMock() mock_resource.is_deployed.return_value = True @@ -348,7 +348,7 @@ def test_get_resource_status_active(self): def test_get_resource_status_inactive(self): """Test _get_resource_status for inactive resource.""" - from tetra_rp.cli.commands.undeploy import _get_resource_status + from runpod_flash.cli.commands.undeploy import _get_resource_status mock_resource = MagicMock() mock_resource.is_deployed.return_value = False @@ -360,7 +360,7 @@ def test_get_resource_status_inactive(self): def test_get_resource_status_exception(self): """Test _get_resource_status when exception occurs.""" - from tetra_rp.cli.commands.undeploy import _get_resource_status + from runpod_flash.cli.commands.undeploy import _get_resource_status mock_resource = MagicMock() mock_resource.is_deployed.side_effect = Exception("API Error") @@ -372,7 +372,7 @@ def test_get_resource_status_exception(self): def test_get_resource_type(self, sample_resources): """Test _get_resource_type returns formatted type.""" - from tetra_rp.cli.commands.undeploy import _get_resource_type + from runpod_flash.cli.commands.undeploy import _get_resource_type resource = list(sample_resources.values())[0] resource_type = _get_resource_type(resource) diff --git a/tests/unit/cli/utils/test_deployment.py b/tests/unit/cli/utils/test_deployment.py index a4466196..5880643e 100644 --- a/tests/unit/cli/utils/test_deployment.py +++ b/tests/unit/cli/utils/test_deployment.py @@ -6,7 +6,7 @@ import pytest -from tetra_rp.cli.utils.deployment import ( +from runpod_flash.cli.utils.deployment import ( provision_resources_for_build, deploy_to_environment, reconcile_and_provision_resources, @@ -54,9 +54,9 @@ async def test_provision_resources_for_build_success( mock_flash_app.get_build_manifest.return_value = manifest with ( - patch("tetra_rp.cli.utils.deployment.ResourceManager") as mock_manager_cls, + patch("runpod_flash.cli.utils.deployment.ResourceManager") as mock_manager_cls, patch( - "tetra_rp.cli.utils.deployment.create_resource_from_manifest" + "runpod_flash.cli.utils.deployment.create_resource_from_manifest" ) as mock_create_resource, ): mock_manager = MagicMock() @@ -130,9 +130,9 @@ async def test_provision_resources_for_build_failure( mock_flash_app.get_build_manifest.return_value = manifest with ( - patch("tetra_rp.cli.utils.deployment.ResourceManager") as mock_manager_cls, + patch("runpod_flash.cli.utils.deployment.ResourceManager") as mock_manager_cls, patch( - "tetra_rp.cli.utils.deployment.create_resource_from_manifest" + "runpod_flash.cli.utils.deployment.create_resource_from_manifest" ) as mock_create_resource, ): mock_manager = MagicMock() @@ -178,9 +178,9 @@ async def mock_get_or_deploy_resource(resource): return resource_mock with ( - patch("tetra_rp.cli.utils.deployment.ResourceManager") as mock_manager_cls, + patch("runpod_flash.cli.utils.deployment.ResourceManager") as mock_manager_cls, patch( - "tetra_rp.cli.utils.deployment.create_resource_from_manifest" + "runpod_flash.cli.utils.deployment.create_resource_from_manifest" ) as mock_create_resource, ): mock_manager = MagicMock() @@ -240,10 +240,10 @@ async def test_deploy_to_environment_success( with ( patch("pathlib.Path.cwd", return_value=tmp_path), - patch("tetra_rp.cli.utils.deployment.FlashApp.from_name") as mock_from_name, - patch("tetra_rp.cli.utils.deployment.ResourceManager") as mock_manager_cls, + patch("runpod_flash.cli.utils.deployment.FlashApp.from_name") as mock_from_name, + patch("runpod_flash.cli.utils.deployment.ResourceManager") as mock_manager_cls, patch( - "tetra_rp.cli.utils.deployment.create_resource_from_manifest" + "runpod_flash.cli.utils.deployment.create_resource_from_manifest" ) as mock_create_resource, ): mock_from_name.return_value = mock_flash_app @@ -292,10 +292,10 @@ async def test_deploy_to_environment_provisioning_failure(mock_flash_app, tmp_pa with ( patch("pathlib.Path.cwd", return_value=tmp_path), - patch("tetra_rp.cli.utils.deployment.FlashApp.from_name") as mock_from_name, - patch("tetra_rp.cli.utils.deployment.ResourceManager") as mock_manager_cls, + patch("runpod_flash.cli.utils.deployment.FlashApp.from_name") as mock_from_name, + patch("runpod_flash.cli.utils.deployment.ResourceManager") as mock_manager_cls, patch( - "tetra_rp.cli.utils.deployment.create_resource_from_manifest" + "runpod_flash.cli.utils.deployment.create_resource_from_manifest" ) as mock_create_resource, ): mock_from_name.return_value = mock_flash_app @@ -361,9 +361,9 @@ async def test_reconciliation_reprovisions_resources_without_endpoints(tmp_path) with ( patch("pathlib.Path.cwd", return_value=tmp_path), - patch("tetra_rp.cli.utils.deployment.ResourceManager") as mock_manager_cls, + patch("runpod_flash.cli.utils.deployment.ResourceManager") as mock_manager_cls, patch( - "tetra_rp.cli.utils.deployment.create_resource_from_manifest" + "runpod_flash.cli.utils.deployment.create_resource_from_manifest" ) as mock_create_resource, ): # Both resources should be re-provisioned (marked as "update" action) diff --git a/tests/unit/core/utils/test_http.py b/tests/unit/core/utils/test_http.py index d26c0954..06f124e5 100644 --- a/tests/unit/core/utils/test_http.py +++ b/tests/unit/core/utils/test_http.py @@ -1,7 +1,7 @@ """Tests for HTTP utilities for RunPod API communication.""" import requests -from tetra_rp.core.utils.http import ( +from runpod_flash.core.utils.http import ( get_authenticated_httpx_client, get_authenticated_requests_session, ) diff --git a/tests/unit/resources/test_app.py b/tests/unit/resources/test_app.py index 099e20f4..f28ba4d0 100644 --- a/tests/unit/resources/test_app.py +++ b/tests/unit/resources/test_app.py @@ -4,12 +4,12 @@ import pytest -from tetra_rp.core.resources.app import FlashApp +from runpod_flash.core.resources.app import FlashApp @pytest.fixture def mock_graphql_client(): - with patch("tetra_rp.core.resources.app.RunpodGraphQLClient") as mock_cls: + with patch("runpod_flash.core.resources.app.RunpodGraphQLClient") as mock_cls: client = AsyncMock() client.__aenter__.return_value = client client.__aexit__.return_value = False diff --git a/tests/unit/resources/test_cpu.py b/tests/unit/resources/test_cpu.py index d35ee608..b30b4eb7 100644 --- a/tests/unit/resources/test_cpu.py +++ b/tests/unit/resources/test_cpu.py @@ -2,7 +2,7 @@ Unit tests for CPU utilities and constants. """ -from tetra_rp.core.resources.cpu import ( +from runpod_flash.core.resources.cpu import ( CpuInstanceType, CPU_INSTANCE_DISK_LIMITS, calculate_max_disk_size, diff --git a/tests/unit/resources/test_cpu_load_balancer.py b/tests/unit/resources/test_cpu_load_balancer.py index 6bf6f34e..ded35c78 100644 --- a/tests/unit/resources/test_cpu_load_balancer.py +++ b/tests/unit/resources/test_cpu_load_balancer.py @@ -7,12 +7,12 @@ import os -from tetra_rp.core.resources.cpu import CpuInstanceType -from tetra_rp.core.resources.load_balancer_sls_resource import ( +from runpod_flash.core.resources.cpu import CpuInstanceType +from runpod_flash.core.resources.load_balancer_sls_resource import ( CpuLoadBalancerSlsResource, ) -from tetra_rp.core.resources.serverless import ServerlessType, ServerlessScalerType -from tetra_rp.core.resources.serverless_cpu import CpuServerlessEndpoint +from runpod_flash.core.resources.serverless import ServerlessType, ServerlessScalerType +from runpod_flash.core.resources.serverless_cpu import CpuServerlessEndpoint # Set a dummy API key for tests that create ResourceManager instances os.environ.setdefault("RUNPOD_API_KEY", "test-key-for-unit-tests") @@ -294,7 +294,7 @@ def test_cpu_load_balancer_auto_sizes_disk_multiple_instances(self): def test_cpu_load_balancer_preserves_custom_disk_size(self): """Test that explicit disk sizes are preserved during auto-sizing.""" - from tetra_rp.core.resources.template import PodTemplate + from runpod_flash.core.resources.template import PodTemplate template = PodTemplate( name="custom", diff --git a/tests/unit/resources/test_gpu_ids.py b/tests/unit/resources/test_gpu_ids.py index a8ae0445..9763f4ed 100644 --- a/tests/unit/resources/test_gpu_ids.py +++ b/tests/unit/resources/test_gpu_ids.py @@ -1,4 +1,4 @@ -from tetra_rp.core.resources.gpu import GpuGroup, GpuType +from runpod_flash.core.resources.gpu import GpuGroup, GpuType class TestGpuIdsImports: diff --git a/tests/unit/resources/test_live_load_balancer.py b/tests/unit/resources/test_live_load_balancer.py index 88795aff..03b8b74c 100644 --- a/tests/unit/resources/test_live_load_balancer.py +++ b/tests/unit/resources/test_live_load_balancer.py @@ -6,12 +6,14 @@ import pytest -from tetra_rp.core.resources.cpu import CpuInstanceType -from tetra_rp.core.resources.live_serverless import ( +from runpod_flash.core.resources.cpu import CpuInstanceType +from runpod_flash.core.resources.live_serverless import ( CpuLiveLoadBalancer, LiveLoadBalancer, ) -from tetra_rp.core.resources.load_balancer_sls_resource import LoadBalancerSlsResource +from runpod_flash.core.resources.load_balancer_sls_resource import ( + LoadBalancerSlsResource, +) class TestLiveLoadBalancer: @@ -19,17 +21,17 @@ class TestLiveLoadBalancer: def test_live_load_balancer_creation_with_local_tag(self, monkeypatch): """Test LiveLoadBalancer creates with local image tag.""" - monkeypatch.setenv("TETRA_IMAGE_TAG", "local") + monkeypatch.setenv("FLASH_IMAGE_TAG", "local") # Need to reload modules to pick up new env var import importlib - import tetra_rp.core.resources.constants as const_module - import tetra_rp.core.resources.live_serverless as ls_module + import runpod_flash.core.resources.constants as const_module + import runpod_flash.core.resources.live_serverless as ls_module importlib.reload(const_module) importlib.reload(ls_module) lb = ls_module.LiveLoadBalancer(name="test-lb") - expected_image = "runpod/tetra-rp-lb:local" + expected_image = "runpod/flash-lb:local" assert lb.imageName == expected_image assert lb.template is not None assert lb.template.imageName == expected_image @@ -37,11 +39,11 @@ def test_live_load_balancer_creation_with_local_tag(self, monkeypatch): def test_live_load_balancer_default_image_tag(self): """Test LiveLoadBalancer uses default image tag.""" # Clear any custom tag - os.environ.pop("TETRA_IMAGE_TAG", None) + os.environ.pop("FLASH_IMAGE_TAG", None) lb = LiveLoadBalancer(name="test-lb") - assert "runpod/tetra-rp-lb:" in lb.imageName + assert "runpod/flash-lb:" in lb.imageName assert lb.template is not None assert lb.template.imageName == lb.imageName @@ -110,11 +112,11 @@ def test_load_balancer_sls_with_image_name(self): """Test LoadBalancerSlsResource creates template from imageName.""" lb = LoadBalancerSlsResource( name="test-lb", - imageName="runpod/tetra-rp-lb:latest", + imageName="runpod/flash-lb:latest", ) assert lb.template is not None - assert lb.template.imageName == "runpod/tetra-rp-lb:latest" + assert lb.template.imageName == "runpod/flash-lb:latest" def test_load_balancer_sls_requires_image_template_or_id(self): """Test LoadBalancerSlsResource requires one of: imageName, template, templateId.""" @@ -157,7 +159,7 @@ def test_live_load_balancer_serialization_roundtrip(self): assert "imageName" not in payload # Verify the template has the correct image - assert "tetra-rp-lb:" in payload["template"]["imageName"], ( + assert "flash-lb:" in payload["template"]["imageName"], ( "Must have load-balancer image" ) @@ -185,17 +187,17 @@ class TestCpuLiveLoadBalancer: def test_cpu_live_load_balancer_creation_with_local_tag(self, monkeypatch): """Test CpuLiveLoadBalancer creates with local image tag.""" - monkeypatch.setenv("TETRA_IMAGE_TAG", "local") + monkeypatch.setenv("FLASH_IMAGE_TAG", "local") # Need to reload modules to pick up new env var import importlib - import tetra_rp.core.resources.constants as const_module - import tetra_rp.core.resources.live_serverless as ls_module + import runpod_flash.core.resources.constants as const_module + import runpod_flash.core.resources.live_serverless as ls_module importlib.reload(const_module) importlib.reload(ls_module) lb = ls_module.CpuLiveLoadBalancer(name="test-lb") - expected_image = "runpod/tetra-rp-lb-cpu:local" + expected_image = "runpod/flash-lb-cpu:local" assert lb.imageName == expected_image assert lb.template is not None assert lb.template.imageName == expected_image @@ -203,11 +205,11 @@ def test_cpu_live_load_balancer_creation_with_local_tag(self, monkeypatch): def test_cpu_live_load_balancer_default_image_tag(self): """Test CpuLiveLoadBalancer uses default CPU LB image tag.""" # Clear any custom tag - os.environ.pop("TETRA_IMAGE_TAG", None) + os.environ.pop("FLASH_IMAGE_TAG", None) lb = CpuLiveLoadBalancer(name="test-lb") - assert "runpod/tetra-rp-lb-cpu:" in lb.imageName + assert "runpod/flash-lb-cpu:" in lb.imageName assert lb.template is not None assert lb.template.imageName == lb.imageName diff --git a/tests/unit/resources/test_live_serverless.py b/tests/unit/resources/test_live_serverless.py index 15b73968..bdcd1464 100644 --- a/tests/unit/resources/test_live_serverless.py +++ b/tests/unit/resources/test_live_serverless.py @@ -3,9 +3,12 @@ """ import pytest -from tetra_rp.core.resources.cpu import CpuInstanceType -from tetra_rp.core.resources.live_serverless import LiveServerless, CpuLiveServerless -from tetra_rp.core.resources.template import PodTemplate +from runpod_flash.core.resources.cpu import CpuInstanceType +from runpod_flash.core.resources.live_serverless import ( + LiveServerless, + CpuLiveServerless, +) +from runpod_flash.core.resources.template import PodTemplate class TestLiveServerless: @@ -21,7 +24,7 @@ def test_live_serverless_gpu_defaults(self): assert live_serverless.instanceIds is None assert live_serverless.template is not None assert live_serverless.template.containerDiskInGb == 64 - assert "tetra-rp:" in live_serverless.imageName # GPU image + assert "flash:" in live_serverless.imageName # GPU image def test_live_serverless_image_locked(self): """Test LiveServerless imageName is locked to GPU image.""" @@ -35,7 +38,7 @@ def test_live_serverless_image_locked(self): live_serverless.imageName = "custom/image:latest" assert live_serverless.imageName == original_image - assert "tetra-rp:" in live_serverless.imageName # Still GPU image + assert "flash:" in live_serverless.imageName # Still GPU image def test_live_serverless_with_custom_template(self): """Test LiveServerless with custom template.""" @@ -68,7 +71,7 @@ def test_cpu_live_serverless_defaults(self): assert live_serverless.template is not None # Default disk size should be 20GB for CPU3G_2_8 assert live_serverless.template.containerDiskInGb == 20 - assert "tetra-rp-cpu:" in live_serverless.imageName # CPU image + assert "flash-cpu:" in live_serverless.imageName # CPU image def test_cpu_live_serverless_custom_instances(self): """Test CpuLiveServerless with custom CPU instances.""" @@ -104,7 +107,7 @@ def test_cpu_live_serverless_image_locked(self): live_serverless.imageName = "custom/image:latest" assert live_serverless.imageName == original_image - assert "tetra-rp-cpu:" in live_serverless.imageName # Still CPU image + assert "flash-cpu:" in live_serverless.imageName # Still CPU image def test_cpu_live_serverless_validation_failure(self): """Test CpuLiveServerless validation fails with excessive disk size.""" @@ -159,13 +162,13 @@ class TestLiveServerlessMixin: def test_live_image_property_gpu(self): """Test LiveServerless _live_image property.""" live_serverless = LiveServerless(name="test") - assert "tetra-rp:" in live_serverless._live_image + assert "flash:" in live_serverless._live_image assert "cpu" not in live_serverless._live_image def test_live_image_property_cpu(self): """Test CpuLiveServerless _live_image property.""" live_serverless = CpuLiveServerless(name="test") - assert "tetra-rp-cpu:" in live_serverless._live_image + assert "flash-cpu:" in live_serverless._live_image def test_image_name_property_gpu(self): """Test LiveServerless imageName property returns locked image.""" diff --git a/tests/unit/resources/test_load_balancer_drift.py b/tests/unit/resources/test_load_balancer_drift.py index 6af99fd8..0a5b4ae3 100644 --- a/tests/unit/resources/test_load_balancer_drift.py +++ b/tests/unit/resources/test_load_balancer_drift.py @@ -6,12 +6,12 @@ import os -from tetra_rp.core.resources.cpu import CpuInstanceType -from tetra_rp.core.resources.load_balancer_sls_resource import ( +from runpod_flash.core.resources.cpu import CpuInstanceType +from runpod_flash.core.resources.load_balancer_sls_resource import ( CpuLoadBalancerSlsResource, LoadBalancerSlsResource, ) -from tetra_rp.core.resources.serverless_cpu import CpuServerlessEndpoint +from runpod_flash.core.resources.serverless_cpu import CpuServerlessEndpoint # Set a dummy API key for tests os.environ.setdefault("RUNPOD_API_KEY", "test-key-for-unit-tests") @@ -45,7 +45,7 @@ def test_lb_config_hash_excludes_template_field(self): hash1 = lb1.config_hash # Simulate API assigning a template - from tetra_rp.core.resources.serverless import PodTemplate + from runpod_flash.core.resources.serverless import PodTemplate lb1.template = PodTemplate(imageName="test/image:latest", name="test") hash_after_template = lb1.config_hash @@ -168,7 +168,7 @@ def test_cpu_lb_config_hash_excludes_template(self): ) hash1 = cpu_lb.config_hash - from tetra_rp.core.resources.serverless import PodTemplate + from runpod_flash.core.resources.serverless import PodTemplate cpu_lb.template = PodTemplate(imageName="test/image:latest", name="test") hash2 = cpu_lb.config_hash @@ -220,7 +220,7 @@ def test_structural_change_ignores_template_field(self): ) # Add template to lb1 - from tetra_rp.core.resources.serverless import PodTemplate + from runpod_flash.core.resources.serverless import PodTemplate lb1.template = PodTemplate(imageName="test/image:latest", name="test") @@ -416,7 +416,7 @@ class TestSerializerDefensiveBehavior: def test_scaler_type_serializer_with_enum(self): """Serializer correctly handles ServerlessScalerType enum.""" - from tetra_rp.core.resources.serverless import ServerlessScalerType + from runpod_flash.core.resources.serverless import ServerlessScalerType lb = LoadBalancerSlsResource( name="test-lb", @@ -448,7 +448,7 @@ def test_scaler_type_serializer_with_string(self): def test_type_serializer_with_enum(self): """Serializer correctly handles ServerlessType enum.""" - from tetra_rp.core.resources.serverless import ServerlessType + from runpod_flash.core.resources.serverless import ServerlessType lb = LoadBalancerSlsResource( name="test-lb", diff --git a/tests/unit/resources/test_network_volume.py b/tests/unit/resources/test_network_volume.py index 0e1ec80f..17adec1d 100644 --- a/tests/unit/resources/test_network_volume.py +++ b/tests/unit/resources/test_network_volume.py @@ -5,7 +5,7 @@ from unittest.mock import AsyncMock, patch, MagicMock import pytest -from tetra_rp.core.resources.network_volume import NetworkVolume, DataCenter +from runpod_flash.core.resources.network_volume import NetworkVolume, DataCenter class TestNetworkVolumeIdempotent: @@ -43,7 +43,7 @@ async def test_deploy_creates_new_volume_when_none_exists( mock_runpod_client.create_network_volume.return_value = sample_volume_data with patch( - "tetra_rp.core.resources.network_volume.RunpodRestClient" + "runpod_flash.core.resources.network_volume.RunpodRestClient" ) as mock_client_class: mock_client_class.return_value.__aenter__.return_value = mock_runpod_client mock_client_class.return_value.__aexit__ = AsyncMock() @@ -68,7 +68,7 @@ async def test_deploy_reuses_existing_volume_with_same_name( mock_runpod_client.list_network_volumes.return_value = [sample_volume_data] with patch( - "tetra_rp.core.resources.network_volume.RunpodRestClient" + "runpod_flash.core.resources.network_volume.RunpodRestClient" ) as mock_client_class: mock_client_class.return_value.__aenter__.return_value = mock_runpod_client mock_client_class.return_value.__aexit__ = AsyncMock() @@ -98,7 +98,7 @@ async def test_deploy_creates_new_when_existing_has_different_datacenter( mock_runpod_client.create_network_volume.return_value = sample_volume_data with patch( - "tetra_rp.core.resources.network_volume.RunpodRestClient" + "runpod_flash.core.resources.network_volume.RunpodRestClient" ) as mock_client_class: mock_client_class.return_value.__aenter__.return_value = mock_runpod_client mock_client_class.return_value.__aexit__ = AsyncMock() @@ -127,7 +127,7 @@ async def test_deploy_multiple_times_same_name_is_idempotent( mock_runpod_client.create_network_volume.return_value = sample_volume_data with patch( - "tetra_rp.core.resources.network_volume.RunpodRestClient" + "runpod_flash.core.resources.network_volume.RunpodRestClient" ) as mock_client_class: mock_client_class.return_value.__aenter__.return_value = mock_runpod_client mock_client_class.return_value.__aexit__ = AsyncMock() @@ -162,7 +162,7 @@ async def test_deploy_uses_resource_manager_to_register(self, sample_volume_data deployed_volume.id = sample_volume_data["id"] with patch( - "tetra_rp.core.resources.network_volume.ResourceManager" + "runpod_flash.core.resources.network_volume.ResourceManager" ) as mock_manager_cls: mock_manager = MagicMock() mock_manager.get_or_deploy_resource = AsyncMock( diff --git a/tests/unit/resources/test_resource_manager.py b/tests/unit/resources/test_resource_manager.py index f72684b2..53846d9c 100644 --- a/tests/unit/resources/test_resource_manager.py +++ b/tests/unit/resources/test_resource_manager.py @@ -3,10 +3,10 @@ import pytest from unittest.mock import MagicMock, patch, AsyncMock -from tetra_rp.core.resources.resource_manager import ResourceManager -from tetra_rp.core.utils.singleton import SingletonMixin -from tetra_rp.core.resources.serverless import ServerlessResource -from tetra_rp.core.exceptions import RunpodAPIKeyError +from runpod_flash.core.resources.resource_manager import ResourceManager +from runpod_flash.core.utils.singleton import SingletonMixin +from runpod_flash.core.resources.serverless import ServerlessResource +from runpod_flash.core.exceptions import RunpodAPIKeyError class TestResourceManager: @@ -31,9 +31,9 @@ def reset_singleton(self): @pytest.fixture def mock_resource_file(self, tmp_path): """Mock the resource state file path.""" - resource_file = tmp_path / ".tetra_resources.pkl" + resource_file = tmp_path / ".runpod" / "resources.pkl" with patch( - "tetra_rp.core.resources.resource_manager.RESOURCE_STATE_FILE", + "runpod_flash.core.resources.resource_manager.RESOURCE_STATE_FILE", resource_file, ): yield resource_file @@ -423,14 +423,14 @@ def reset_singleton(self): def test_cpu_config_hash_excludes_env(self): """Test that CPU endpoint config_hash excludes env to prevent drift.""" - from tetra_rp.core.resources.serverless_cpu import CpuServerlessEndpoint + from runpod_flash.core.resources.serverless_cpu import CpuServerlessEndpoint config1 = CpuServerlessEndpoint( name="test-cpu", workersMax=3, workersMin=0, flashboot=False, - imageName="runpod/tetra-rp-cpu:latest", + imageName="runpod/flash-cpu:latest", ) config2 = CpuServerlessEndpoint( @@ -438,7 +438,7 @@ def test_cpu_config_hash_excludes_env(self): workersMax=3, workersMin=0, flashboot=False, - imageName="runpod/tetra-rp-cpu:latest", + imageName="runpod/flash-cpu:latest", env={"DIFFERENT_ENV": "value"}, ) diff --git a/tests/unit/resources/test_serverless.py b/tests/unit/resources/test_serverless.py index 5d74669c..c01bc054 100644 --- a/tests/unit/resources/test_serverless.py +++ b/tests/unit/resources/test_serverless.py @@ -7,7 +7,7 @@ from unittest.mock import AsyncMock, MagicMock, patch from typing import Any, Dict -from tetra_rp.core.resources.serverless import ( +from runpod_flash.core.resources.serverless import ( ServerlessResource, ServerlessEndpoint, ServerlessScalerType, @@ -18,10 +18,10 @@ ServerlessHealth, Status, ) -from tetra_rp.core.resources.serverless_cpu import CpuServerlessEndpoint -from tetra_rp.core.resources.gpu import GpuGroup -from tetra_rp.core.resources.cpu import CpuInstanceType -from tetra_rp.core.resources.network_volume import NetworkVolume, DataCenter +from runpod_flash.core.resources.serverless_cpu import CpuServerlessEndpoint +from runpod_flash.core.resources.gpu import GpuGroup +from runpod_flash.core.resources.cpu import CpuInstanceType +from runpod_flash.core.resources.network_volume import NetworkVolume, DataCenter class TestServerlessResource: @@ -103,7 +103,7 @@ def test_resource_id_changes_only_for_hashed_fields(self): Create new instances with same env to test different configs. """ # Fixed env to ensure consistency - env = {"TETRA_IMAGE_TAG": "test-123"} + env = {"FLASH_IMAGE_TAG": "test-123"} # Original config serverless1 = ServerlessResource(name="hash-test", flashboot=False, env=env) @@ -490,7 +490,7 @@ async def test_deploy_success_with_network_volume( mock_runpod_client.save_endpoint.return_value = deployment_response with patch( - "tetra_rp.core.resources.serverless.RunpodGraphQLClient" + "runpod_flash.core.resources.serverless.RunpodGraphQLClient" ) as mock_client_class: mock_client_class.return_value.__aenter__.return_value = mock_runpod_client mock_client_class.return_value.__aexit__.return_value = None @@ -538,7 +538,7 @@ async def test_do_deploy_restores_input_only_fields(self, mock_runpod_client): mock_runpod_client.save_endpoint = AsyncMock(return_value=deployment_response) with patch( - "tetra_rp.core.resources.serverless.RunpodGraphQLClient" + "runpod_flash.core.resources.serverless.RunpodGraphQLClient" ) as mock_client_class: mock_client_class.return_value.__aenter__.return_value = mock_runpod_client mock_client_class.return_value.__aexit__.return_value = None @@ -565,7 +565,7 @@ async def test_deploy_failure_raises_exception(self, mock_runpod_client): mock_runpod_client.save_endpoint.side_effect = Exception("API Error") with patch( - "tetra_rp.core.resources.serverless.RunpodGraphQLClient" + "runpod_flash.core.resources.serverless.RunpodGraphQLClient" ) as mock_client_class: mock_client_class.return_value.__aenter__.return_value = mock_runpod_client mock_client_class.return_value.__aexit__.return_value = None @@ -715,7 +715,7 @@ def test_serverless_endpoint_with_template_id(self): def test_serverless_endpoint_with_existing_template(self): """Test ServerlessEndpoint with existing template.""" - from tetra_rp.core.resources.template import PodTemplate + from runpod_flash.core.resources.template import PodTemplate template = PodTemplate(name="existing-template", imageName="test/image:v1") endpoint = ServerlessEndpoint( @@ -732,7 +732,7 @@ def test_serverless_endpoint_with_existing_template(self): def test_serverless_endpoint_template_env_override(self): """Test ServerlessEndpoint overrides template env vars.""" - from tetra_rp.core.resources.template import PodTemplate, KeyValuePair + from runpod_flash.core.resources.template import PodTemplate, KeyValuePair template = PodTemplate( name="existing-template", @@ -929,12 +929,12 @@ async def test_undeploy_success(self): mock_client.__aexit__ = AsyncMock(return_value=None) with patch( - "tetra_rp.core.resources.serverless.RunpodGraphQLClient" + "runpod_flash.core.resources.serverless.RunpodGraphQLClient" ) as MockClient: MockClient.return_value = mock_client # undeploy() now goes through resource_manager and returns a dict with patch( - "tetra_rp.core.resources.serverless.ResourceManager" + "runpod_flash.core.resources.serverless.ResourceManager" ) as MockManager: manager_instance = AsyncMock() manager_instance.undeploy_resource = AsyncMock( @@ -967,12 +967,12 @@ async def test_undeploy_api_failure_when_endpoint_exists(self): mock_client.__aexit__ = AsyncMock(return_value=None) with patch( - "tetra_rp.core.resources.serverless.RunpodGraphQLClient" + "runpod_flash.core.resources.serverless.RunpodGraphQLClient" ) as MockClient: MockClient.return_value = mock_client # undeploy() now goes through resource_manager and returns a dict with patch( - "tetra_rp.core.resources.serverless.ResourceManager" + "runpod_flash.core.resources.serverless.ResourceManager" ) as MockManager: manager_instance = AsyncMock() manager_instance.undeploy_resource = AsyncMock( @@ -1006,12 +1006,12 @@ async def test_undeploy_auto_cleanup_when_endpoint_not_found(self): mock_client.__aexit__ = AsyncMock(return_value=None) with patch( - "tetra_rp.core.resources.serverless.RunpodGraphQLClient" + "runpod_flash.core.resources.serverless.RunpodGraphQLClient" ) as MockClient: MockClient.return_value = mock_client # undeploy() now goes through resource_manager and returns a dict with patch( - "tetra_rp.core.resources.serverless.ResourceManager" + "runpod_flash.core.resources.serverless.ResourceManager" ) as MockManager: manager_instance = AsyncMock() manager_instance.undeploy_resource = AsyncMock( @@ -1035,7 +1035,9 @@ async def test_undeploy_no_id(self): # No ID set # undeploy() now goes through resource_manager and returns a dict - with patch("tetra_rp.core.resources.serverless.ResourceManager") as MockManager: + with patch( + "runpod_flash.core.resources.serverless.ResourceManager" + ) as MockManager: manager_instance = AsyncMock() manager_instance.undeploy_resource = AsyncMock( return_value={ diff --git a/tests/unit/resources/test_serverless_cpu.py b/tests/unit/resources/test_serverless_cpu.py index d2942e25..722d175c 100644 --- a/tests/unit/resources/test_serverless_cpu.py +++ b/tests/unit/resources/test_serverless_cpu.py @@ -4,9 +4,9 @@ import pickle import pytest -from tetra_rp.core.resources.cpu import CpuInstanceType -from tetra_rp.core.resources.serverless_cpu import CpuServerlessEndpoint -from tetra_rp.core.resources.template import PodTemplate +from runpod_flash.core.resources.cpu import CpuInstanceType +from runpod_flash.core.resources.serverless_cpu import CpuServerlessEndpoint +from runpod_flash.core.resources.template import PodTemplate class TestCpuServerlessEndpoint: @@ -429,7 +429,7 @@ def test_template_object_comparison_issue(self): When comparing templates using !=, different instances are never equal even if they have the same content, causing false structural changes. """ - from tetra_rp.core.resources.template import PodTemplate + from runpod_flash.core.resources.template import PodTemplate template1 = PodTemplate( name="test-template", diff --git a/tests/unit/resources/test_smart_disk_sizing.py b/tests/unit/resources/test_smart_disk_sizing.py index 6c298cac..4e253ca1 100644 --- a/tests/unit/resources/test_smart_disk_sizing.py +++ b/tests/unit/resources/test_smart_disk_sizing.py @@ -6,9 +6,9 @@ """ import pytest -from tetra_rp.core.resources.cpu import CpuInstanceType -from tetra_rp.core.resources.serverless import ServerlessEndpoint -from tetra_rp.core.resources.template import PodTemplate +from runpod_flash.core.resources.cpu import CpuInstanceType +from runpod_flash.core.resources.serverless import ServerlessEndpoint +from runpod_flash.core.resources.template import PodTemplate class TestServerlessEndpointUniversalCpuDetection: diff --git a/tests/unit/runtime/test_circuit_breaker.py b/tests/unit/runtime/test_circuit_breaker.py index 39d405ce..ec9c6f81 100644 --- a/tests/unit/runtime/test_circuit_breaker.py +++ b/tests/unit/runtime/test_circuit_breaker.py @@ -4,7 +4,7 @@ import pytest -from tetra_rp.runtime.circuit_breaker import ( +from runpod_flash.runtime.circuit_breaker import ( CircuitBreakerOpenError, CircuitState, EndpointCircuitBreaker, diff --git a/tests/unit/runtime/test_generic_handler.py b/tests/unit/runtime/test_generic_handler.py index 9b000559..89b7bae4 100644 --- a/tests/unit/runtime/test_generic_handler.py +++ b/tests/unit/runtime/test_generic_handler.py @@ -4,7 +4,7 @@ import cloudpickle -from tetra_rp.runtime.generic_handler import ( +from runpod_flash.runtime.generic_handler import ( create_handler, deserialize_arguments, execute_function, diff --git a/tests/unit/runtime/test_lb_handler.py b/tests/unit/runtime/test_lb_handler.py index 55388b21..4d23dd8f 100644 --- a/tests/unit/runtime/test_lb_handler.py +++ b/tests/unit/runtime/test_lb_handler.py @@ -1,6 +1,6 @@ """Unit tests for LoadBalancer handler factory.""" -from tetra_rp.runtime.lb_handler import create_lb_handler +from runpod_flash.runtime.lb_handler import create_lb_handler class TestExecuteEndpointStillWorks: diff --git a/tests/unit/runtime/test_load_balancer.py b/tests/unit/runtime/test_load_balancer.py index 335c138d..89806f2a 100644 --- a/tests/unit/runtime/test_load_balancer.py +++ b/tests/unit/runtime/test_load_balancer.py @@ -2,8 +2,8 @@ import pytest -from tetra_rp.runtime.load_balancer import LoadBalancer -from tetra_rp.runtime.reliability_config import LoadBalancerStrategy +from runpod_flash.runtime.load_balancer import LoadBalancer +from runpod_flash.runtime.reliability_config import LoadBalancerStrategy class TestLoadBalancer: @@ -106,7 +106,7 @@ def __init__(self, open_endpoints): self.open_endpoints = open_endpoints def get_state(self, endpoint): - from tetra_rp.runtime.circuit_breaker import CircuitState + from runpod_flash.runtime.circuit_breaker import CircuitState if endpoint in self.open_endpoints: return CircuitState.OPEN @@ -126,7 +126,7 @@ async def test_all_endpoints_unhealthy_returns_none(self): class MockCircuitBreaker: def get_state(self, endpoint): - from tetra_rp.runtime.circuit_breaker import CircuitState + from runpod_flash.runtime.circuit_breaker import CircuitState return CircuitState.OPEN diff --git a/tests/unit/runtime/test_manifest_fetcher.py b/tests/unit/runtime/test_manifest_fetcher.py index f7ae27a1..98a02ee2 100644 --- a/tests/unit/runtime/test_manifest_fetcher.py +++ b/tests/unit/runtime/test_manifest_fetcher.py @@ -7,7 +7,7 @@ import pytest -from tetra_rp.runtime.manifest_fetcher import ManifestFetcher +from runpod_flash.runtime.manifest_fetcher import ManifestFetcher class TestManifestFetcher: diff --git a/tests/unit/runtime/test_mothership_provisioner.py b/tests/unit/runtime/test_mothership_provisioner.py index 680a8e0a..580037a8 100644 --- a/tests/unit/runtime/test_mothership_provisioner.py +++ b/tests/unit/runtime/test_mothership_provisioner.py @@ -8,7 +8,7 @@ import pytest -from tetra_rp.runtime.mothership_provisioner import ( +from runpod_flash.runtime.mothership_provisioner import ( ManifestDiff, compute_resource_hash, create_resource_from_manifest, @@ -385,12 +385,12 @@ class TestCreateResourceFromManifest: def test_create_resource_from_manifest_serverless(self): """Test creating ServerlessResource from manifest.""" - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.core.resources.serverless import ServerlessResource resource_name = "worker1" resource_data = { "resource_type": "ServerlessResource", - "imageName": "runpod/tetra-rp:latest", + "imageName": "runpod/flash:latest", } mothership_url = "https://test.api.runpod.ai" @@ -411,12 +411,12 @@ def test_create_resource_from_manifest_live_serverless(self): This is a known limitation - manifest needs to include full deployment config to properly construct different resource types. """ - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.core.resources.serverless import ServerlessResource resource_name = "worker1" resource_data = { "resource_type": "LiveServerless", - "imageName": "runpod/tetra-rp:latest", + "imageName": "runpod/flash:latest", } mothership_url = "https://test.api.runpod.ai" @@ -441,11 +441,11 @@ def test_create_resource_from_manifest_unsupported_type(self): def test_create_resource_from_manifest_default_type(self): """Test that default type is ServerlessResource when not specified.""" - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.core.resources.serverless import ServerlessResource resource_name = "worker1" resource_data = { - "imageName": "runpod/tetra-rp:latest" + "imageName": "runpod/flash:latest" } # No resource_type specified mothership_url = "https://test.api.runpod.ai" @@ -464,12 +464,12 @@ def test_create_resource_from_manifest_cli_context_no_runpod_endpoint_id(self): FLASH_MOTHERSHIP_ID should not be included in env to avoid Pydantic validation errors (None values are not allowed). """ - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.core.resources.serverless import ServerlessResource resource_name = "worker1" resource_data = { "resource_type": "ServerlessResource", - "imageName": "runpod/tetra-rp:latest", + "imageName": "runpod/flash:latest", } mothership_url = "" # Empty URL indicates CLI context @@ -492,12 +492,12 @@ def test_create_resource_from_manifest_mothership_context_with_endpoint_id(self) FLASH_MOTHERSHIP_ID should be included in env so children can query the State Manager using the mothership's ID. """ - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.core.resources.serverless import ServerlessResource resource_name = "worker1" resource_data = { "resource_type": "ServerlessResource", - "imageName": "runpod/tetra-rp:latest", + "imageName": "runpod/flash:latest", } mothership_url = "https://mothership.api.runpod.ai" @@ -514,7 +514,7 @@ def test_create_resource_from_manifest_mothership_context_with_endpoint_id(self) def test_create_resource_from_manifest_cpu_live_serverless(self): """Test creating CpuLiveServerless from manifest.""" - from tetra_rp.core.resources.live_serverless import CpuLiveServerless + from runpod_flash.core.resources.live_serverless import CpuLiveServerless resource_name = "cpu_worker" resource_data = {"resource_type": "CpuLiveServerless"} diff --git a/tests/unit/runtime/test_production_wrapper.py b/tests/unit/runtime/test_production_wrapper.py index 5b7945fa..6b930427 100644 --- a/tests/unit/runtime/test_production_wrapper.py +++ b/tests/unit/runtime/test_production_wrapper.py @@ -4,12 +4,12 @@ import pytest -from tetra_rp.runtime.production_wrapper import ( +from runpod_flash.runtime.production_wrapper import ( ProductionWrapper, create_production_wrapper, reset_wrapper, ) -from tetra_rp.runtime.service_registry import ServiceRegistry +from runpod_flash.runtime.service_registry import ServiceRegistry class TestProductionWrapper: @@ -209,7 +209,7 @@ async def test_execute_remote_payload_format(self, wrapper, sample_function): mock_resource.run_sync = AsyncMock() mock_resource.run_sync.return_value = MagicMock(error="", output=None) - with patch("tetra_rp.runtime.serialization.cloudpickle") as mock_pickle: + with patch("runpod_flash.runtime.serialization.cloudpickle") as mock_pickle: mock_pickle.dumps.return_value = b"pickled" await wrapper._execute_remote( @@ -286,7 +286,7 @@ def test_create_wrapper_with_custom_registry(self): def test_create_wrapper_creates_defaults(self): """Test that wrapper creates default components.""" with patch( - "tetra_rp.runtime.production_wrapper.ServiceRegistry" + "runpod_flash.runtime.production_wrapper.ServiceRegistry" ) as mock_registry_class: create_production_wrapper() diff --git a/tests/unit/runtime/test_reliability_config.py b/tests/unit/runtime/test_reliability_config.py index 52473bf3..91099a45 100644 --- a/tests/unit/runtime/test_reliability_config.py +++ b/tests/unit/runtime/test_reliability_config.py @@ -1,6 +1,6 @@ """Tests for reliability configuration module.""" -from tetra_rp.runtime.reliability_config import ( +from runpod_flash.runtime.reliability_config import ( CircuitBreakerConfig, LoadBalancerConfig, LoadBalancerStrategy, @@ -95,7 +95,7 @@ def test_defaults(self): """Test default values.""" config = MetricsConfig() assert config.enabled is True - assert config.namespace == "tetra.metrics" + assert config.namespace == "flash.metrics" def test_custom_values(self): """Test with custom values.""" @@ -132,7 +132,7 @@ def test_custom_nested_configs(self): def test_from_env_default(self, monkeypatch): """Test from_env with no environment variables.""" - monkeypatch.delenv("TETRA_CIRCUIT_BREAKER_ENABLED", raising=False) + monkeypatch.delenv("FLASH_CIRCUIT_BREAKER_ENABLED", raising=False) config = ReliabilityConfig.from_env() assert config.circuit_breaker.enabled is True assert config.load_balancer.enabled is False @@ -140,9 +140,9 @@ def test_from_env_default(self, monkeypatch): def test_from_env_custom(self, monkeypatch): """Test from_env with custom environment variables.""" - monkeypatch.setenv("TETRA_CIRCUIT_BREAKER_ENABLED", "false") - monkeypatch.setenv("TETRA_LOAD_BALANCER_ENABLED", "true") - monkeypatch.setenv("TETRA_CB_FAILURE_THRESHOLD", "10") + monkeypatch.setenv("FLASH_CIRCUIT_BREAKER_ENABLED", "false") + monkeypatch.setenv("FLASH_LOAD_BALANCER_ENABLED", "true") + monkeypatch.setenv("FLASH_CB_FAILURE_THRESHOLD", "10") config = ReliabilityConfig.from_env() assert config.circuit_breaker.enabled is False assert config.load_balancer.enabled is True @@ -150,13 +150,13 @@ def test_from_env_custom(self, monkeypatch): def test_from_env_load_balancer_strategy(self, monkeypatch): """Test from_env with load balancer strategy.""" - monkeypatch.setenv("TETRA_LB_STRATEGY", "least_connections") + monkeypatch.setenv("FLASH_LB_STRATEGY", "least_connections") config = ReliabilityConfig.from_env() assert config.load_balancer.strategy == LoadBalancerStrategy.LEAST_CONNECTIONS def test_from_env_invalid_strategy_defaults(self, monkeypatch): """Test from_env with invalid strategy defaults to round_robin.""" - monkeypatch.setenv("TETRA_LB_STRATEGY", "invalid_strategy") + monkeypatch.setenv("FLASH_LB_STRATEGY", "invalid_strategy") config = ReliabilityConfig.from_env() assert config.load_balancer.strategy == LoadBalancerStrategy.ROUND_ROBIN diff --git a/tests/unit/runtime/test_retry_manager.py b/tests/unit/runtime/test_retry_manager.py index 122cf902..cdfb326a 100644 --- a/tests/unit/runtime/test_retry_manager.py +++ b/tests/unit/runtime/test_retry_manager.py @@ -4,7 +4,7 @@ import pytest -from tetra_rp.runtime.retry_manager import RetryExhaustedError, retry_with_backoff +from runpod_flash.runtime.retry_manager import RetryExhaustedError, retry_with_backoff class TestRetryWithBackoff: @@ -149,7 +149,7 @@ async def test_retry_with_circuit_breaker_open(self): class MockCircuitBreaker: def get_state(self): - from tetra_rp.runtime.circuit_breaker import CircuitState + from runpod_flash.runtime.circuit_breaker import CircuitState return CircuitState.OPEN diff --git a/tests/unit/runtime/test_serialization.py b/tests/unit/runtime/test_serialization.py index 1fef1e97..43246c79 100644 --- a/tests/unit/runtime/test_serialization.py +++ b/tests/unit/runtime/test_serialization.py @@ -4,8 +4,8 @@ import pytest -from tetra_rp.runtime.exceptions import SerializationError -from tetra_rp.runtime.serialization import ( +from runpod_flash.runtime.exceptions import SerializationError +from runpod_flash.runtime.serialization import ( deserialize_arg, deserialize_args, deserialize_kwargs, @@ -54,14 +54,18 @@ def test_serialize_empty_args(self): def test_serialize_args_propagates_serialization_error(self): """Test serialize_args propagates SerializationError.""" - with patch("tetra_rp.runtime.serialization.serialize_arg") as mock_serialize: + with patch( + "runpod_flash.runtime.serialization.serialize_arg" + ) as mock_serialize: mock_serialize.side_effect = SerializationError("Known error") with pytest.raises(SerializationError, match="Known error"): serialize_args((1, 2)) def test_serialize_args_unexpected_error(self): """Test serialize_args handles unexpected exceptions.""" - with patch("tetra_rp.runtime.serialization.serialize_arg") as mock_serialize: + with patch( + "runpod_flash.runtime.serialization.serialize_arg" + ) as mock_serialize: mock_serialize.side_effect = RuntimeError("Unexpected error") with pytest.raises(SerializationError, match="Failed to serialize args"): serialize_args((1, 2)) @@ -85,14 +89,18 @@ def test_serialize_empty_kwargs(self): def test_serialize_kwargs_propagates_serialization_error(self): """Test serialize_kwargs propagates SerializationError.""" - with patch("tetra_rp.runtime.serialization.serialize_arg") as mock_serialize: + with patch( + "runpod_flash.runtime.serialization.serialize_arg" + ) as mock_serialize: mock_serialize.side_effect = SerializationError("Known error") with pytest.raises(SerializationError, match="Known error"): serialize_kwargs({"key": 42}) def test_serialize_kwargs_unexpected_error(self): """Test serialize_kwargs handles unexpected exceptions.""" - with patch("tetra_rp.runtime.serialization.serialize_arg") as mock_serialize: + with patch( + "runpod_flash.runtime.serialization.serialize_arg" + ) as mock_serialize: mock_serialize.side_effect = RuntimeError("Unexpected error") with pytest.raises(SerializationError, match="Failed to serialize kwargs"): serialize_kwargs({"key": 42}) @@ -132,7 +140,7 @@ def test_deserialize_empty_args(self): def test_deserialize_args_propagates_serialization_error(self): """Test deserialize_args propagates SerializationError.""" with patch( - "tetra_rp.runtime.serialization.deserialize_arg" + "runpod_flash.runtime.serialization.deserialize_arg" ) as mock_deserialize: mock_deserialize.side_effect = SerializationError("Known error") with pytest.raises(SerializationError, match="Known error"): @@ -141,7 +149,7 @@ def test_deserialize_args_propagates_serialization_error(self): def test_deserialize_args_unexpected_error(self): """Test deserialize_args handles unexpected exceptions.""" with patch( - "tetra_rp.runtime.serialization.deserialize_arg" + "runpod_flash.runtime.serialization.deserialize_arg" ) as mock_deserialize: mock_deserialize.side_effect = RuntimeError("Unexpected error") with pytest.raises(SerializationError, match="Failed to deserialize args"): @@ -165,7 +173,7 @@ def test_deserialize_empty_kwargs(self): def test_deserialize_kwargs_propagates_serialization_error(self): """Test deserialize_kwargs propagates SerializationError.""" with patch( - "tetra_rp.runtime.serialization.deserialize_arg" + "runpod_flash.runtime.serialization.deserialize_arg" ) as mock_deserialize: mock_deserialize.side_effect = SerializationError("Known error") with pytest.raises(SerializationError, match="Known error"): @@ -174,7 +182,7 @@ def test_deserialize_kwargs_propagates_serialization_error(self): def test_deserialize_kwargs_unexpected_error(self): """Test deserialize_kwargs handles unexpected exceptions.""" with patch( - "tetra_rp.runtime.serialization.deserialize_arg" + "runpod_flash.runtime.serialization.deserialize_arg" ) as mock_deserialize: mock_deserialize.side_effect = RuntimeError("Unexpected error") with pytest.raises( diff --git a/tests/unit/runtime/test_service_registry.py b/tests/unit/runtime/test_service_registry.py index 17540834..97261cc7 100644 --- a/tests/unit/runtime/test_service_registry.py +++ b/tests/unit/runtime/test_service_registry.py @@ -9,7 +9,7 @@ import pytest -from tetra_rp.runtime.service_registry import ServiceRegistry +from runpod_flash.runtime.service_registry import ServiceRegistry class TestServiceRegistry: @@ -323,7 +323,7 @@ def test_init_no_manifest_client_no_runpod_key(self, manifest_file): """Test initialization without RUNPOD_API_KEY.""" with patch.dict(os.environ, {}, clear=True): with patch( - "tetra_rp.runtime.service_registry.StateManagerClient" + "runpod_flash.runtime.service_registry.StateManagerClient" ) as mock_client_class: mock_client_class.side_effect = Exception("No API key") registry = ServiceRegistry(manifest_path=manifest_file) diff --git a/tests/unit/runtime/test_state_manager_client.py b/tests/unit/runtime/test_state_manager_client.py index 61eb8350..92250c91 100644 --- a/tests/unit/runtime/test_state_manager_client.py +++ b/tests/unit/runtime/test_state_manager_client.py @@ -5,9 +5,9 @@ import pytest -from tetra_rp.core.api.runpod import RunpodGraphQLClient -from tetra_rp.runtime.exceptions import ManifestServiceUnavailableError -from tetra_rp.runtime.state_manager_client import StateManagerClient +from runpod_flash.core.api.runpod import RunpodGraphQLClient +from runpod_flash.runtime.exceptions import ManifestServiceUnavailableError +from runpod_flash.runtime.state_manager_client import StateManagerClient class TestStateManagerClientBasicOperations: @@ -35,7 +35,7 @@ async def test_get_persisted_manifest_success(self): } with patch( - "tetra_rp.runtime.state_manager_client.RunpodGraphQLClient", + "runpod_flash.runtime.state_manager_client.RunpodGraphQLClient", return_value=mock_client, ): client = StateManagerClient() @@ -71,7 +71,7 @@ async def test_update_resource_state_merges_existing_data(self): mock_client.update_build_manifest = AsyncMock() with patch( - "tetra_rp.runtime.state_manager_client.RunpodGraphQLClient", + "runpod_flash.runtime.state_manager_client.RunpodGraphQLClient", return_value=mock_client, ): client = StateManagerClient() @@ -107,7 +107,7 @@ async def test_remove_resource_state_deletes_resource(self): mock_client.update_build_manifest = AsyncMock() with patch( - "tetra_rp.runtime.state_manager_client.RunpodGraphQLClient", + "runpod_flash.runtime.state_manager_client.RunpodGraphQLClient", return_value=mock_client, ): client = StateManagerClient() @@ -132,7 +132,7 @@ async def test_fetch_build_and_manifest_raises_when_no_active_build(self): mock_client.get_flash_environment.return_value = {"activeBuildId": None} with patch( - "tetra_rp.runtime.state_manager_client.RunpodGraphQLClient", + "runpod_flash.runtime.state_manager_client.RunpodGraphQLClient", return_value=mock_client, ): client = StateManagerClient() @@ -153,7 +153,7 @@ async def test_fetch_build_and_manifest_raises_when_no_manifest(self): mock_client.get_flash_build.return_value = {"id": "build-123", "manifest": None} with patch( - "tetra_rp.runtime.state_manager_client.RunpodGraphQLClient", + "runpod_flash.runtime.state_manager_client.RunpodGraphQLClient", return_value=mock_client, ): client = StateManagerClient() @@ -175,13 +175,13 @@ async def test_get_persisted_manifest_timeout(self): ) with patch( - "tetra_rp.runtime.state_manager_client.RunpodGraphQLClient", + "runpod_flash.runtime.state_manager_client.RunpodGraphQLClient", return_value=mock_client, ): client = StateManagerClient(max_retries=2) with patch( - "tetra_rp.runtime.state_manager_client.asyncio.sleep", + "runpod_flash.runtime.state_manager_client.asyncio.sleep", new_callable=AsyncMock, ): with pytest.raises( @@ -206,13 +206,13 @@ async def test_update_resource_state_graphql_error(self): ) with patch( - "tetra_rp.runtime.state_manager_client.RunpodGraphQLClient", + "runpod_flash.runtime.state_manager_client.RunpodGraphQLClient", return_value=mock_client, ): client = StateManagerClient(max_retries=2) with patch( - "tetra_rp.runtime.state_manager_client.asyncio.sleep", + "runpod_flash.runtime.state_manager_client.asyncio.sleep", new_callable=AsyncMock, ): with pytest.raises( @@ -246,13 +246,13 @@ async def test_get_persisted_manifest_retry_on_transient_failure(self): ] with patch( - "tetra_rp.runtime.state_manager_client.RunpodGraphQLClient", + "runpod_flash.runtime.state_manager_client.RunpodGraphQLClient", return_value=mock_client, ): client = StateManagerClient(max_retries=3) with patch( - "tetra_rp.runtime.state_manager_client.asyncio.sleep", + "runpod_flash.runtime.state_manager_client.asyncio.sleep", new_callable=AsyncMock, ) as mock_sleep: manifest = await client.get_persisted_manifest("env-123") @@ -275,13 +275,13 @@ async def test_update_resource_state_exhaust_retries(self): mock_client.get_flash_environment.side_effect = ConnectionError("Always fails") with patch( - "tetra_rp.runtime.state_manager_client.RunpodGraphQLClient", + "runpod_flash.runtime.state_manager_client.RunpodGraphQLClient", return_value=mock_client, ): client = StateManagerClient(max_retries=2) with patch( - "tetra_rp.runtime.state_manager_client.asyncio.sleep", + "runpod_flash.runtime.state_manager_client.asyncio.sleep", new_callable=AsyncMock, ): with pytest.raises( @@ -309,13 +309,13 @@ async def test_remove_resource_state_retry_with_backoff(self): mock_client.update_build_manifest = AsyncMock() with patch( - "tetra_rp.runtime.state_manager_client.RunpodGraphQLClient", + "runpod_flash.runtime.state_manager_client.RunpodGraphQLClient", return_value=mock_client, ): client = StateManagerClient(max_retries=3) with patch( - "tetra_rp.runtime.state_manager_client.asyncio.sleep", + "runpod_flash.runtime.state_manager_client.asyncio.sleep", new_callable=AsyncMock, ) as mock_sleep: await client.remove_resource_state("env-123", "worker1") @@ -344,7 +344,7 @@ async def test_concurrent_update_resource_state_with_lock(self): mock_client.update_build_manifest = AsyncMock() with patch( - "tetra_rp.runtime.state_manager_client.RunpodGraphQLClient", + "runpod_flash.runtime.state_manager_client.RunpodGraphQLClient", return_value=mock_client, ): client = StateManagerClient() @@ -384,7 +384,7 @@ async def test_concurrent_remove_and_update_serialized(self): mock_client.update_build_manifest = AsyncMock() with patch( - "tetra_rp.runtime.state_manager_client.RunpodGraphQLClient", + "runpod_flash.runtime.state_manager_client.RunpodGraphQLClient", return_value=mock_client, ): client = StateManagerClient() diff --git a/tests/unit/test_api_key_validation.py b/tests/unit/test_api_key_validation.py index 2d7dafcc..40a6681d 100644 --- a/tests/unit/test_api_key_validation.py +++ b/tests/unit/test_api_key_validation.py @@ -6,8 +6,8 @@ import pytest -from tetra_rp.core.exceptions import RunpodAPIKeyError -from tetra_rp.core.validation import validate_api_key, validate_api_key_with_context +from runpod_flash.core.exceptions import RunpodAPIKeyError +from runpod_flash.core.validation import validate_api_key, validate_api_key_with_context class TestRunpodAPIKeyError: @@ -125,7 +125,7 @@ def test_graphql_client_raises_on_missing_key( self, monkeypatch: pytest.MonkeyPatch ): """Test that RunpodGraphQLClient raises RunpodAPIKeyError.""" - from tetra_rp.core.api.runpod import RunpodGraphQLClient + from runpod_flash.core.api.runpod import RunpodGraphQLClient monkeypatch.delenv("RUNPOD_API_KEY", raising=False) @@ -136,7 +136,7 @@ def test_graphql_client_raises_on_missing_key( def test_rest_client_raises_on_missing_key(self, monkeypatch: pytest.MonkeyPatch): """Test that RunpodRestClient raises RunpodAPIKeyError.""" - from tetra_rp.core.api.runpod import RunpodRestClient + from runpod_flash.core.api.runpod import RunpodRestClient monkeypatch.delenv("RUNPOD_API_KEY", raising=False) @@ -147,7 +147,7 @@ def test_rest_client_raises_on_missing_key(self, monkeypatch: pytest.MonkeyPatch def test_graphql_client_accepts_explicit_key(self): """Test that RunpodGraphQLClient accepts API key parameter.""" - from tetra_rp.core.api.runpod import RunpodGraphQLClient + from runpod_flash.core.api.runpod import RunpodGraphQLClient client = RunpodGraphQLClient(api_key="explicit_test_key") @@ -155,7 +155,7 @@ def test_graphql_client_accepts_explicit_key(self): def test_rest_client_accepts_explicit_key(self): """Test that RunpodRestClient accepts API key parameter.""" - from tetra_rp.core.api.runpod import RunpodRestClient + from runpod_flash.core.api.runpod import RunpodRestClient client = RunpodRestClient(api_key="explicit_test_key") diff --git a/tests/unit/test_async_function_support.py b/tests/unit/test_async_function_support.py index dda76c0e..4a39f7f0 100644 --- a/tests/unit/test_async_function_support.py +++ b/tests/unit/test_async_function_support.py @@ -3,7 +3,7 @@ """ import pytest -from tetra_rp.stubs.live_serverless import get_function_source +from runpod_flash.stubs.live_serverless import get_function_source class TestAsyncFunctionSupport: diff --git a/tests/unit/test_class_caching.py b/tests/unit/test_class_caching.py index e6490055..16f2f4d4 100644 --- a/tests/unit/test_class_caching.py +++ b/tests/unit/test_class_caching.py @@ -10,8 +10,8 @@ import tempfile from unittest.mock import patch -from tetra_rp.core.resources import ServerlessResource -from tetra_rp.execute_class import ( +from runpod_flash.core.resources import ServerlessResource +from runpod_flash.execute_class import ( _SERIALIZED_CLASS_CACHE, create_remote_class, get_class_cache_key, @@ -34,7 +34,7 @@ def __init__(self, value): cache_key = get_class_cache_key(SimpleClass, (42,), {}) # Extract class code separately for verification - from tetra_rp.execute_class import extract_class_code_simple + from runpod_flash.execute_class import extract_class_code_simple class_code = extract_class_code_simple(SimpleClass) @@ -227,7 +227,9 @@ def __init__(self, value): OptimizationTestClass, self.mock_resource_config, [], [], True, {} ) - with patch("tetra_rp.execute_class.extract_class_code_simple") as mock_extract: + with patch( + "runpod_flash.execute_class.extract_class_code_simple" + ) as mock_extract: mock_extract.return_value = "class OptimizationTestClass:\n pass" # Create instance - should call extract_class_code_simple for caching diff --git a/tests/unit/test_concurrency_issues.py b/tests/unit/test_concurrency_issues.py index 449e0e76..f096a67c 100644 --- a/tests/unit/test_concurrency_issues.py +++ b/tests/unit/test_concurrency_issues.py @@ -1,5 +1,5 @@ """ -Unit tests for concurrency issues in tetra_rp. +Unit tests for concurrency issues in runpod_flash. This module tests race conditions and thread safety issues in: 1. ResourceManager singleton and resource provisioning @@ -21,17 +21,17 @@ import pytest -from tetra_rp.core.resources.resource_manager import ( +from runpod_flash.core.resources.resource_manager import ( ResourceManager, RESOURCE_STATE_FILE, ) -from tetra_rp.core.resources.base import DeployableResource -from tetra_rp.core.utils.singleton import SingletonMixin -from tetra_rp.stubs.live_serverless import ( +from runpod_flash.core.resources.base import DeployableResource +from runpod_flash.core.utils.singleton import SingletonMixin +from runpod_flash.stubs.live_serverless import ( _SERIALIZED_FUNCTION_CACHE, _function_cache_lock, ) -from tetra_rp.execute_class import _SERIALIZED_CLASS_CACHE +from runpod_flash.execute_class import _SERIALIZED_CLASS_CACHE class MockDeployableResource(DeployableResource): @@ -154,14 +154,14 @@ def setup_method(self): self.original_state_file = RESOURCE_STATE_FILE # Patch the state file location - import tetra_rp.core.resources.resource_manager as rm_module + import runpod_flash.core.resources.resource_manager as rm_module rm_module.RESOURCE_STATE_FILE = Path(self.temp_dir) / "test_resources.pkl" def teardown_method(self): """Clean up test environment.""" # Restore original state file - import tetra_rp.core.resources.resource_manager as rm_module + import runpod_flash.core.resources.resource_manager as rm_module rm_module.RESOURCE_STATE_FILE = self.original_state_file @@ -449,7 +449,7 @@ async def test_concurrent_remote_function_simulation(self): # Mock the entire remote execution pipeline with patch( - "tetra_rp.core.resources.resource_manager.ResourceManager.get_or_deploy_resource" + "runpod_flash.core.resources.resource_manager.ResourceManager.get_or_deploy_resource" ) as mock_deploy: # Configure mock to have deployment delay async def slow_deploy(config): diff --git a/tests/unit/test_deployment.py b/tests/unit/test_deployment.py index 43862f78..1ad72fa1 100644 --- a/tests/unit/test_deployment.py +++ b/tests/unit/test_deployment.py @@ -4,11 +4,11 @@ import asyncio from unittest.mock import MagicMock, AsyncMock, patch -from tetra_rp.core.deployment import ( +from runpod_flash.core.deployment import ( DeploymentOrchestrator, DeploymentStatus, ) -from tetra_rp.core.resources.serverless import ServerlessResource +from runpod_flash.core.resources.serverless import ServerlessResource class TestDeploymentOrchestrator: diff --git a/tests/unit/test_discovery.py b/tests/unit/test_discovery.py index 60721ad9..b338603d 100644 --- a/tests/unit/test_discovery.py +++ b/tests/unit/test_discovery.py @@ -3,8 +3,8 @@ import pytest from textwrap import dedent -from tetra_rp.core.discovery import ResourceDiscovery -from tetra_rp.core.resources.serverless import ServerlessResource +from runpod_flash.core.discovery import ResourceDiscovery +from runpod_flash.core.resources.serverless import ServerlessResource class TestResourceDiscovery: @@ -53,8 +53,8 @@ def test_discover_single_remote_decorator(self, temp_entry_point): temp_entry_point.write_text( dedent( """ - from tetra_rp.client import remote - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.client import remote + from runpod_flash.core.resources.serverless import ServerlessResource gpu_config = ServerlessResource( name="test-gpu", @@ -83,8 +83,8 @@ def test_discover_multiple_remote_decorators(self, temp_entry_point): temp_entry_point.write_text( dedent( """ - from tetra_rp.client import remote - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.client import remote + from runpod_flash.core.resources.serverless import ServerlessResource gpu_config = ServerlessResource( name="gpu-endpoint", @@ -125,8 +125,8 @@ def test_discover_positional_argument(self, temp_entry_point): temp_entry_point.write_text( dedent( """ - from tetra_rp.client import remote - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.client import remote + from runpod_flash.core.resources.serverless import ServerlessResource my_config = ServerlessResource( name="test-endpoint", @@ -156,7 +156,7 @@ def test_discover_invalid_import(self, temp_entry_point): """ import nonexistent_module - from tetra_rp.client import remote + from runpod_flash.client import remote """ ) ) @@ -172,8 +172,8 @@ def test_discover_cache(self, temp_entry_point): temp_entry_point.write_text( dedent( """ - from tetra_rp.client import remote - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.client import remote + from runpod_flash.core.resources.serverless import ServerlessResource config = ServerlessResource( name="cached-endpoint", @@ -205,8 +205,8 @@ def test_clear_cache(self, temp_entry_point): temp_entry_point.write_text( dedent( """ - from tetra_rp.client import remote - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.client import remote + from runpod_flash.core.resources.serverless import ServerlessResource config = ServerlessResource( name="test-endpoint", @@ -253,7 +253,7 @@ def test_discover_non_deployable_resource(self, temp_entry_point): temp_entry_point.write_text( dedent( """ - from tetra_rp.client import remote + from runpod_flash.client import remote # Not a DeployableResource config = {"name": "not-a-resource"} @@ -310,8 +310,8 @@ def test_discover_with_directory_scan(self, tmp_path): worker_file.write_text( dedent( """ - from tetra_rp.client import remote - from tetra_rp.core.resources.serverless import ServerlessResource + from runpod_flash.client import remote + from runpod_flash.core.resources.serverless import ServerlessResource gpu_config = ServerlessResource( name="test-gpu-worker", diff --git a/tests/unit/test_dotenv_loading.py b/tests/unit/test_dotenv_loading.py index e587b75a..790005f4 100644 --- a/tests/unit/test_dotenv_loading.py +++ b/tests/unit/test_dotenv_loading.py @@ -21,7 +21,7 @@ def test_dotenv_loads_before_imports(self): # Note: Resources are imported directly at module initialization init_file = ( - Path(__file__).parent.parent.parent / "src" / "tetra_rp" / "__init__.py" + Path(__file__).parent.parent.parent / "src" / "runpod_flash" / "__init__.py" ) content = init_file.read_text() lines = content.split("\n") @@ -61,8 +61,8 @@ def test_env_file_loading_with_temp_file(self): env_content = """ # Test environment variables RUNPOD_API_KEY=test_api_key_from_file -TETRA_GPU_IMAGE=test_gpu_image:v1.0 -TETRA_CPU_IMAGE=test_cpu_image:v2.0 +FLASH_GPU_IMAGE=test_gpu_image:v1.0 +FLASH_CPU_IMAGE=test_cpu_image:v2.0 LOG_LEVEL=DEBUG RUNPOD_API_BASE_URL=https://test-api.runpod.io CUSTOM_TEST_VAR=file_value @@ -77,8 +77,8 @@ def test_env_file_loading_with_temp_file(self): # Clear any existing env vars that might interfere test_vars = [ "RUNPOD_API_KEY", - "TETRA_GPU_IMAGE", - "TETRA_CPU_IMAGE", + "FLASH_GPU_IMAGE", + "FLASH_CPU_IMAGE", "LOG_LEVEL", "RUNPOD_API_BASE_URL", "CUSTOM_TEST_VAR", @@ -101,8 +101,8 @@ def test_env_file_loading_with_temp_file(self): # Verify the environment variables were loaded assert os.environ.get("RUNPOD_API_KEY") == "test_api_key_from_file" - assert os.environ.get("TETRA_GPU_IMAGE") == "test_gpu_image:v1.0" - assert os.environ.get("TETRA_CPU_IMAGE") == "test_cpu_image:v2.0" + assert os.environ.get("FLASH_GPU_IMAGE") == "test_gpu_image:v1.0" + assert os.environ.get("FLASH_CPU_IMAGE") == "test_cpu_image:v2.0" assert os.environ.get("LOG_LEVEL") == "DEBUG" assert ( os.environ.get("RUNPOD_API_BASE_URL") @@ -151,14 +151,14 @@ def test_shell_env_vars_override_file_vars(self): elif "TEST_OVERRIDE_VAR" in os.environ: del os.environ["TEST_OVERRIDE_VAR"] - def test_env_vars_available_after_tetra_import(self): - """Test that env vars are available when tetra_rp modules are imported.""" + def test_env_vars_available_after_flash_import(self): + """Test that env vars are available when runpod_flash modules are imported.""" # Set up test environment variables test_env_vars = { "RUNPOD_API_KEY": "test_key_12345", - "TETRA_GPU_IMAGE": "test/gpu:latest", - "TETRA_CPU_IMAGE": "test/cpu:latest", + "FLASH_GPU_IMAGE": "test/gpu:latest", + "FLASH_CPU_IMAGE": "test/cpu:latest", "LOG_LEVEL": "WARNING", } @@ -168,32 +168,34 @@ def test_env_vars_available_after_tetra_import(self): os.environ[var] = value try: - # Remove tetra_rp from sys.modules to force fresh import + # Remove runpod_flash from sys.modules to force fresh import modules_to_remove = [ - name for name in sys.modules.keys() if name.startswith("tetra_rp") + name for name in sys.modules.keys() if name.startswith("runpod_flash") ] for module_name in modules_to_remove: del sys.modules[module_name] - # Import tetra_rp (this will trigger __init__.py and load_dotenv()) + # Import runpod_flash (this will trigger __init__.py and load_dotenv()) # Clear any cached modules to ensure fresh import with new env vars modules_to_clear = [ - name for name in sys.modules.keys() if name.startswith("tetra_rp.core") + name + for name in sys.modules.keys() + if name.startswith("runpod_flash.core") ] for module_name in modules_to_clear: del sys.modules[module_name] # Import specific modules that use environment variables - from tetra_rp.core.api.runpod import RunpodGraphQLClient - from tetra_rp.core.resources.live_serverless import ( - TETRA_GPU_IMAGE, - TETRA_CPU_IMAGE, + from runpod_flash.core.api.runpod import RunpodGraphQLClient + from runpod_flash.core.resources.live_serverless import ( + FLASH_GPU_IMAGE, + FLASH_CPU_IMAGE, ) # Verify that the environment variables are accessible in imported modules - assert TETRA_GPU_IMAGE == "test/gpu:latest" - assert TETRA_CPU_IMAGE == "test/cpu:latest" + assert FLASH_GPU_IMAGE == "test/gpu:latest" + assert FLASH_CPU_IMAGE == "test/cpu:latest" # Test that RunpodGraphQLClient can access the API key try: @@ -288,13 +290,13 @@ def test_env_vars_used_by_key_modules(self): modules_to_clear = [ name for name in sys.modules.keys() - if name.startswith("tetra_rp.core.api") + if name.startswith("runpod_flash.core.api") ] for module_name in modules_to_clear: del sys.modules[module_name] # Test RunpodGraphQLClient uses RUNPOD_API_KEY - from tetra_rp.core.api.runpod import RunpodGraphQLClient + from runpod_flash.core.api.runpod import RunpodGraphQLClient try: client = RunpodGraphQLClient() @@ -304,7 +306,7 @@ def test_env_vars_used_by_key_modules(self): assert os.environ.get("RUNPOD_API_KEY") == "test_api_key_12345" # Test RUNPOD_API_BASE_URL is used (now imports with fresh env) - from tetra_rp.core.api.runpod import RUNPOD_API_BASE_URL + from runpod_flash.core.api.runpod import RUNPOD_API_BASE_URL assert RUNPOD_API_BASE_URL == "https://custom-api.runpod.io" @@ -324,7 +326,7 @@ def test_dotenv_import_present_in_init(self): """Test that dotenv import is actually present in __init__.py.""" init_file = ( - Path(__file__).parent.parent.parent / "src" / "tetra_rp" / "__init__.py" + Path(__file__).parent.parent.parent / "src" / "runpod_flash" / "__init__.py" ) content = init_file.read_text() diff --git a/tests/unit/test_execute_class.py b/tests/unit/test_execute_class.py index ab818bec..6f499f47 100644 --- a/tests/unit/test_execute_class.py +++ b/tests/unit/test_execute_class.py @@ -1,5 +1,5 @@ """ -Unit tests for tetra_rp.execute_class module. +Unit tests for runpod_flash.execute_class module. """ import asyncio @@ -9,9 +9,9 @@ import cloudpickle import pytest -from tetra_rp.core.resources import ServerlessResource -from tetra_rp.execute_class import create_remote_class, extract_class_code_simple -from tetra_rp.protos.remote_execution import FunctionRequest +from runpod_flash.core.resources import ServerlessResource +from runpod_flash.execute_class import create_remote_class, extract_class_code_simple +from runpod_flash.protos.remote_execution import FunctionRequest class TestExtractClassCodeSimple: @@ -135,10 +135,10 @@ def test_extract_class_fallback_on_error(self): # Mock inspect.getsource to raise an exception with patch( - "tetra_rp.execute_class.inspect.getsource", + "runpod_flash.execute_class.inspect.getsource", side_effect=OSError("No source available"), ): - with patch("tetra_rp.execute_class.log.warning") as mock_log_warning: + with patch("runpod_flash.execute_class.log.warning") as mock_log_warning: result = extract_class_code_simple(mock_class) # Should use fallback diff --git a/tests/unit/test_file_locking.py b/tests/unit/test_file_locking.py index fde24d8c..2162eee6 100644 --- a/tests/unit/test_file_locking.py +++ b/tests/unit/test_file_locking.py @@ -18,7 +18,7 @@ import pytest -from tetra_rp.core.utils.file_lock import ( +from runpod_flash.core.utils.file_lock import ( file_lock, FileLockError, FileLockTimeout, @@ -49,28 +49,28 @@ def test_get_platform_info(self): ] assert sum(locking_mechanisms) >= 1 # At least fallback should work - @patch("tetra_rp.core.utils.file_lock.platform.system") + @patch("runpod_flash.core.utils.file_lock.platform.system") def test_platform_detection_windows(self, mock_system): """Test Windows platform detection.""" mock_system.return_value = "Windows" # Re-import to trigger platform detection from importlib import reload - import tetra_rp.core.utils.file_lock as file_lock_module + import runpod_flash.core.utils.file_lock as file_lock_module reload(file_lock_module) info = file_lock_module.get_platform_info() assert info["platform"] == "Windows" - @patch("tetra_rp.core.utils.file_lock.platform.system") + @patch("runpod_flash.core.utils.file_lock.platform.system") def test_platform_detection_linux(self, mock_system): """Test Linux platform detection.""" mock_system.return_value = "Linux" # Re-import to trigger platform detection from importlib import reload - import tetra_rp.core.utils.file_lock as file_lock_module + import runpod_flash.core.utils.file_lock as file_lock_module reload(file_lock_module) @@ -260,7 +260,7 @@ def teardown_method(self): @pytest.mark.skipif(platform.system() != "Windows", reason="Windows-specific test") def test_windows_locking_available(self): """Test Windows locking is available on Windows platform.""" - import tetra_rp.core.utils.file_lock as file_lock_module + import runpod_flash.core.utils.file_lock as file_lock_module assert file_lock_module._IS_WINDOWS # msvcrt should be available on Windows @@ -270,7 +270,7 @@ def test_windows_locking_available(self): @pytest.mark.skipif(platform.system() == "Windows", reason="Unix-specific test") def test_unix_locking_available(self): """Test Unix locking is available on Unix platforms.""" - import tetra_rp.core.utils.file_lock as file_lock_module + import runpod_flash.core.utils.file_lock as file_lock_module assert file_lock_module._IS_UNIX # fcntl should be available on Unix diff --git a/tests/unit/test_live_serverless_stub.py b/tests/unit/test_live_serverless_stub.py index 398db02e..495c56f5 100644 --- a/tests/unit/test_live_serverless_stub.py +++ b/tests/unit/test_live_serverless_stub.py @@ -1,8 +1,8 @@ """Unit tests for live_serverless stub functionality.""" import ast -from tetra_rp.stubs.live_serverless import get_function_source -from tetra_rp import remote, LiveServerless +from runpod_flash.stubs.live_serverless import get_function_source +from runpod_flash import remote, LiveServerless # Create a dummy config for testing diff --git a/tests/unit/test_load_balancer_sls_resource.py b/tests/unit/test_load_balancer_sls_resource.py index d73f694b..135715ea 100644 --- a/tests/unit/test_load_balancer_sls_resource.py +++ b/tests/unit/test_load_balancer_sls_resource.py @@ -7,12 +7,12 @@ import pytest from unittest.mock import AsyncMock, MagicMock, patch -from tetra_rp.core.resources import ( +from runpod_flash.core.resources import ( LoadBalancerSlsResource, ServerlessType, ServerlessScalerType, ) -from tetra_rp.core.resources.serverless import ServerlessResource +from runpod_flash.core.resources.serverless import ServerlessResource # Set a dummy API key for tests that create ResourceManager instances os.environ.setdefault("RUNPOD_API_KEY", "test-key-for-unit-tests") @@ -153,7 +153,7 @@ async def test_check_ping_endpoint_success(self): new_callable=lambda: property(lambda self: "https://test-endpoint.com"), ), patch( - "tetra_rp.core.utils.http.httpx.AsyncClient", + "runpod_flash.core.utils.http.httpx.AsyncClient", return_value=mock_client, ), ): @@ -178,7 +178,7 @@ async def test_check_ping_endpoint_initializing(self): new_callable=lambda: property(lambda self: "https://test-endpoint.com"), ), patch( - "tetra_rp.core.utils.http.httpx.AsyncClient", + "runpod_flash.core.utils.http.httpx.AsyncClient", return_value=mock_client, ), ): @@ -203,7 +203,7 @@ async def test_check_ping_endpoint_failure(self): new_callable=lambda: property(lambda self: "https://test-endpoint.com"), ), patch( - "tetra_rp.core.utils.http.httpx.AsyncClient", + "runpod_flash.core.utils.http.httpx.AsyncClient", return_value=mock_client, ), ): @@ -230,7 +230,7 @@ async def test_check_ping_endpoint_connection_error(self): new_callable=lambda: property(lambda self: "https://test-endpoint.com"), ), patch( - "tetra_rp.core.utils.http.httpx.AsyncClient", + "runpod_flash.core.utils.http.httpx.AsyncClient", return_value=mock_client, ), ): diff --git a/tests/unit/test_load_balancer_sls_stub.py b/tests/unit/test_load_balancer_sls_stub.py index 172126ab..206f8eea 100644 --- a/tests/unit/test_load_balancer_sls_stub.py +++ b/tests/unit/test_load_balancer_sls_stub.py @@ -6,8 +6,8 @@ import cloudpickle -from tetra_rp import LoadBalancerSlsResource -from tetra_rp.stubs.load_balancer_sls import LoadBalancerSlsStub +from runpod_flash import LoadBalancerSlsResource +from runpod_flash.stubs.load_balancer_sls import LoadBalancerSlsStub # Create test resources @@ -180,7 +180,9 @@ async def test_execute_function_timeout(self): import httpx - with patch("tetra_rp.stubs.load_balancer_sls.httpx.AsyncClient") as mock_client: + with patch( + "runpod_flash.stubs.load_balancer_sls.httpx.AsyncClient" + ) as mock_client: mock_client.return_value.__aenter__.return_value.post = AsyncMock( side_effect=httpx.TimeoutException("Timeout") ) @@ -207,7 +209,9 @@ async def test_execute_function_http_error(self): mock_response.status_code = 500 mock_response.text = "Internal server error" - with patch("tetra_rp.stubs.load_balancer_sls.httpx.AsyncClient") as mock_client: + with patch( + "runpod_flash.stubs.load_balancer_sls.httpx.AsyncClient" + ) as mock_client: error = httpx.HTTPStatusError( "Error", request=MagicMock(), response=mock_response ) @@ -270,8 +274,8 @@ class TestLoadBalancerSlsStubRouting: def test_should_use_execute_for_live_load_balancer(self): """Test that LiveLoadBalancer always uses /execute endpoint.""" - from tetra_rp import LiveLoadBalancer - from tetra_rp import remote + from runpod_flash import LiveLoadBalancer + from runpod_flash import remote lb = LiveLoadBalancer(name="test-live") stub = LoadBalancerSlsStub(lb) @@ -284,7 +288,7 @@ def test_func(): def test_should_use_user_route_for_deployed_lb(self): """Test that deployed LoadBalancerSlsResource uses user-defined route.""" - from tetra_rp import remote + from runpod_flash import remote lb = LoadBalancerSlsResource(name="test-deployed", imageName="test:latest") stub = LoadBalancerSlsStub(lb) @@ -332,7 +336,9 @@ def add(x, y): mock_response = MagicMock() mock_response.json.return_value = {"result": 8} - with patch("tetra_rp.stubs.load_balancer_sls.httpx.AsyncClient") as mock_client: + with patch( + "runpod_flash.stubs.load_balancer_sls.httpx.AsyncClient" + ) as mock_client: mock_client.return_value.__aenter__.return_value.request = AsyncMock( return_value=mock_response ) @@ -364,7 +370,9 @@ def greet(name, greeting="Hello"): mock_response = MagicMock() mock_response.json.return_value = "Hi, Alice!" - with patch("tetra_rp.stubs.load_balancer_sls.httpx.AsyncClient") as mock_client: + with patch( + "runpod_flash.stubs.load_balancer_sls.httpx.AsyncClient" + ) as mock_client: mock_client.return_value.__aenter__.return_value.request = AsyncMock( return_value=mock_response ) diff --git a/tests/unit/test_resource_identity.py b/tests/unit/test_resource_identity.py index 28d13d55..ac977818 100644 --- a/tests/unit/test_resource_identity.py +++ b/tests/unit/test_resource_identity.py @@ -2,8 +2,8 @@ import cloudpickle -from tetra_rp.core.resources.live_serverless import LiveServerless -from tetra_rp.core.resources.gpu import GpuGroup +from runpod_flash.core.resources.live_serverless import LiveServerless +from runpod_flash.core.resources.gpu import GpuGroup class TestResourceIdentity: diff --git a/tests/unit/test_skeleton.py b/tests/unit/test_skeleton.py index 63a39c09..0c3962b4 100644 --- a/tests/unit/test_skeleton.py +++ b/tests/unit/test_skeleton.py @@ -14,7 +14,7 @@ import pytest -from tetra_rp.cli.utils.skeleton import ( +from runpod_flash.cli.utils.skeleton import ( IGNORE_PATTERNS, _should_ignore, create_project_skeleton, @@ -120,7 +120,7 @@ def test_detect_conflicts_nonexistent_template_dir(self, tmp_path, monkeypatch): """Test handling when template directory doesn't exist.""" # Patch __file__ to point to a location where skeleton_template doesn't exist mock_file = str(tmp_path / "mock_skeleton.py") - monkeypatch.setattr("tetra_rp.cli.utils.skeleton.__file__", mock_file) + monkeypatch.setattr("runpod_flash.cli.utils.skeleton.__file__", mock_file) # Should return empty list, not raise exception conflicts = detect_file_conflicts(tmp_path) diff --git a/uv.lock b/uv.lock index 91782172..b3e0568a 100644 --- a/uv.lock +++ b/uv.lock @@ -3174,6 +3174,65 @@ dependencies = [ { name = "watchdog" }, ] +[[package]] +name = "runpod-flash" +version = "0.25.1" +source = { editable = "." } +dependencies = [ + { name = "cloudpickle" }, + { name = "pathspec" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "questionary" }, + { name = "rich" }, + { name = "runpod" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typer" }, +] + +[package.dev-dependencies] +dev = [ + { name = "mcp" }, + { name = "mypy" }, + { name = "ruff" }, +] +test = [ + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, + { name = "pytest-mock" }, + { name = "pytest-xdist" }, + { name = "twine" }, +] + +[package.metadata] +requires-dist = [ + { name = "cloudpickle", specifier = ">=3.1.1" }, + { name = "pathspec", specifier = ">=0.11.0" }, + { name = "pydantic", specifier = ">=2.0.0" }, + { name = "python-dotenv", specifier = ">=1.0.0" }, + { name = "questionary", specifier = ">=2.0.0" }, + { name = "rich", specifier = ">=14.0.0" }, + { name = "runpod", git = "https://github.com/runpod/runpod-python?rev=main" }, + { name = "tomli", marker = "python_full_version < '3.11'", specifier = ">=2.0.0" }, + { name = "typer", specifier = ">=0.12.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "mcp", specifier = ">=1.0.0" }, + { name = "mypy", specifier = ">=1.16.1" }, + { name = "ruff", specifier = ">=0.11.9" }, +] +test = [ + { name = "pytest", specifier = ">=8.4.1" }, + { name = "pytest-asyncio", specifier = ">=1.0.0" }, + { name = "pytest-cov", specifier = ">=6.2.1" }, + { name = "pytest-mock", specifier = ">=3.14.0" }, + { name = "pytest-xdist", specifier = ">=3.6.1" }, + { name = "twine", specifier = ">=6.1.0" }, +] + [[package]] name = "s3transfer" version = "0.16.0" @@ -3256,65 +3315,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033 }, ] -[[package]] -name = "tetra-rp" -version = "0.25.2" -source = { editable = "." } -dependencies = [ - { name = "cloudpickle" }, - { name = "pathspec" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "questionary" }, - { name = "rich" }, - { name = "runpod" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typer" }, -] - -[package.dev-dependencies] -dev = [ - { name = "mcp" }, - { name = "mypy" }, - { name = "ruff" }, -] -test = [ - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "pytest-mock" }, - { name = "pytest-xdist" }, - { name = "twine" }, -] - -[package.metadata] -requires-dist = [ - { name = "cloudpickle", specifier = ">=3.1.1" }, - { name = "pathspec", specifier = ">=0.11.0" }, - { name = "pydantic", specifier = ">=2.0.0" }, - { name = "python-dotenv", specifier = ">=1.0.0" }, - { name = "questionary", specifier = ">=2.0.0" }, - { name = "rich", specifier = ">=14.0.0" }, - { name = "runpod", git = "https://github.com/runpod/runpod-python?rev=main" }, - { name = "tomli", marker = "python_full_version < '3.11'", specifier = ">=2.0.0" }, - { name = "typer", specifier = ">=0.12.0" }, -] - -[package.metadata.requires-dev] -dev = [ - { name = "mcp", specifier = ">=1.0.0" }, - { name = "mypy", specifier = ">=1.16.1" }, - { name = "ruff", specifier = ">=0.11.9" }, -] -test = [ - { name = "pytest", specifier = ">=8.4.1" }, - { name = "pytest-asyncio", specifier = ">=1.0.0" }, - { name = "pytest-cov", specifier = ">=6.2.1" }, - { name = "pytest-mock", specifier = ">=3.14.0" }, - { name = "pytest-xdist", specifier = ">=3.6.1" }, - { name = "twine", specifier = ">=6.1.0" }, -] - [[package]] name = "tomli" version = "2.4.0"