Changes and fixes before the release (docs/small tweaks) (#750)
- update README.md with new docker instructions - update docs/configuration.md - update .github/workflows to have pinned action versions - gofmt events package - fix small bugs in CI scripts - reduce config options for internal/perf/monitor and config. A ring buffer is used to keep 1hr of entries at max 5s granularity. For long term stats use prometheus monitoring on /metrics Fixes #744
This commit is contained in:
@@ -11,7 +11,7 @@ jobs:
|
|||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9
|
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f #v10.2.0
|
||||||
with:
|
with:
|
||||||
days-before-issue-stale: 14
|
days-before-issue-stale: 14
|
||||||
days-before-issue-close: 14
|
days-before-issue-close: 14
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
|
||||||
|
|
||||||
- name: Validate JSON Schema
|
- name: Validate JSON Schema
|
||||||
run: |
|
run: |
|
||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
echo "✓ config-schema.json is valid"
|
echo "✓ config-schema.json is valid"
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 #v6.2.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
|
||||||
|
|
||||||
- name: Free up disk space
|
- name: Free up disk space
|
||||||
if: matrix.platform == 'rocm'
|
if: matrix.platform == 'rocm'
|
||||||
@@ -58,13 +58,13 @@ jobs:
|
|||||||
# no-op for amd64-only builds, so leaving it on for every matrix
|
# no-op for amd64-only builds, so leaving it on for every matrix
|
||||||
# entry keeps the workflow simple.
|
# entry keeps the workflow simple.
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v4
|
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a #v4.0.0
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v4
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd #v4.0.0
|
||||||
|
|
||||||
- name: Log in to GitHub Container Registry
|
- name: Log in to GitHub Container Registry
|
||||||
uses: docker/login-action@v4
|
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 #v4.1.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
|||||||
@@ -31,17 +31,17 @@ jobs:
|
|||||||
run-tests:
|
run-tests:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c #6.4.0
|
||||||
with:
|
with:
|
||||||
go-version: '1.23'
|
go-version-file: go.mod
|
||||||
|
|
||||||
# cache simple-responder to save the build time
|
# cache simple-responder to save the build time
|
||||||
- name: Restore Simple Responder
|
- name: Restore Simple Responder
|
||||||
id: restore-simple-responder
|
id: restore-simple-responder
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||||
with:
|
with:
|
||||||
path: ./build
|
path: ./build
|
||||||
key: ${{ runner.os }}-simple-responder-${{ hashFiles('cmd/simple-responder/simple-responder.go') }}
|
key: ${{ runner.os }}-simple-responder-${{ hashFiles('cmd/simple-responder/simple-responder.go') }}
|
||||||
@@ -56,7 +56,7 @@ jobs:
|
|||||||
# nothing new to save ... skip this step
|
# nothing new to save ... skip this step
|
||||||
if: steps.restore-simple-responder.outputs.cache-hit != 'true'
|
if: steps.restore-simple-responder.outputs.cache-hit != 'true'
|
||||||
id: save-simple-responder
|
id: save-simple-responder
|
||||||
uses: actions/cache/save@v4
|
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||||
with:
|
with:
|
||||||
path: ./build
|
path: ./build
|
||||||
key: ${{ runner.os }}-simple-responder-${{ hashFiles('cmd/simple-responder/simple-responder.go') }}
|
key: ${{ runner.os }}-simple-responder-${{ hashFiles('cmd/simple-responder/simple-responder.go') }}
|
||||||
|
|||||||
@@ -30,24 +30,24 @@ jobs:
|
|||||||
run-tests:
|
run-tests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c #6.4.0
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
|
|
||||||
# Only run in this linux based runner
|
# Only run in this linux based runner
|
||||||
- name: Check Formatting
|
- name: Check Formatting
|
||||||
run: |
|
run: |
|
||||||
if [ "$(gofmt -l . | grep -v 'event/.*_test.go' | wc -l)" -gt 0 ]; then
|
if [ "$(gofmt -l . | wc -l)" -gt 0 ]; then
|
||||||
gofmt -l . | grep -v 'event/.*_test.go'
|
gofmt -l .
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# cache simple-responder to save the build time
|
# cache simple-responder to save the build time
|
||||||
- name: Restore Simple Responder
|
- name: Restore Simple Responder
|
||||||
id: restore-simple-responder
|
id: restore-simple-responder
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||||
with:
|
with:
|
||||||
path: ./build
|
path: ./build
|
||||||
key: ${{ runner.os }}-simple-responder-${{ hashFiles('cmd/simple-responder/simple-responder.go') }}
|
key: ${{ runner.os }}-simple-responder-${{ hashFiles('cmd/simple-responder/simple-responder.go') }}
|
||||||
@@ -60,7 +60,7 @@ jobs:
|
|||||||
# nothing new to save ... skip this step
|
# nothing new to save ... skip this step
|
||||||
if: steps.restore-simple-responder.outputs.cache-hit != 'true'
|
if: steps.restore-simple-responder.outputs.cache-hit != 'true'
|
||||||
id: save-simple-responder
|
id: save-simple-responder
|
||||||
uses: actions/cache/save@v4
|
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
|
||||||
with:
|
with:
|
||||||
path: ./build
|
path: ./build
|
||||||
key: ${{ runner.os }}-simple-responder-${{ hashFiles('cmd/simple-responder/simple-responder.go') }}
|
key: ${{ runner.os }}-simple-responder-${{ hashFiles('cmd/simple-responder/simple-responder.go') }}
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: ${{ github.event.inputs.tag || github.ref }}
|
ref: ${{ github.event.inputs.tag || github.ref }}
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c #6.4.0
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # 6.4.0
|
||||||
with:
|
with:
|
||||||
node-version: "24"
|
node-version: "24"
|
||||||
- name: Install dependencies and build UI
|
- name: Install dependencies and build UI
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
npm run build
|
npm run build
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v6
|
uses: goreleaser/goreleaser-action@1a80836c5c9d9e5755a25cb59ec6f45a3b5f41a8 #7.2.1
|
||||||
with:
|
with:
|
||||||
# either 'goreleaser' (default) or 'goreleaser-pro'
|
# either 'goreleaser' (default) or 'goreleaser-pro'
|
||||||
distribution: goreleaser
|
distribution: goreleaser
|
||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: "Trigger tap repository update"
|
- name: "Trigger tap repository update"
|
||||||
uses: peter-evans/repository-dispatch@v2
|
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 #4.0.1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.TAP_REPO_PAT }}
|
token: ${{ secrets.TAP_REPO_PAT }}
|
||||||
repository: mostlygeek/homebrew-llama-swap
|
repository: mostlygeek/homebrew-llama-swap
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ jobs:
|
|||||||
run-tests:
|
run-tests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # 6.4.0
|
||||||
with:
|
with:
|
||||||
node-version: '24'
|
node-version: '24'
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ jobs:
|
|||||||
backend: ${{ fromJSON(needs.setup.outputs.matrix) }}
|
backend: ${{ fromJSON(needs.setup.outputs.matrix) }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
|
||||||
|
|
||||||
- name: Free up disk space
|
- name: Free up disk space
|
||||||
run: |
|
run: |
|
||||||
@@ -94,11 +94,11 @@ jobs:
|
|||||||
# llama-swap-builder (which has ccache warm) to avoid exhausting disk.
|
# llama-swap-builder (which has ccache warm) to avoid exhausting disk.
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
if: ${{ !env.ACT }}
|
if: ${{ !env.ACT }}
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd #v4.0.0
|
||||||
|
|
||||||
- name: Log in to GitHub Container Registry
|
- name: Log in to GitHub Container Registry
|
||||||
if: ${{ !env.ACT }}
|
if: ${{ !env.ACT }}
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 #v4.1.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
|||||||
@@ -57,8 +57,9 @@ Built in Go for performance and simplicity, llama-swap has zero dependencies and
|
|||||||
- ✅ Customizable
|
- ✅ Customizable
|
||||||
- Run concurrent models with a custom DSL swap matrix ([#643](https://github.com/mostlygeek/llama-swap/issues/643))
|
- Run concurrent models with a custom DSL swap matrix ([#643](https://github.com/mostlygeek/llama-swap/issues/643))
|
||||||
- Automatic unloading of models after timeout by setting a `ttl`
|
- Automatic unloading of models after timeout by setting a `ttl`
|
||||||
- Reliable Docker and Podman support using `cmd` and `cmdStop` together
|
- Docker and Podman support using `cmd` and `cmdStop` together
|
||||||
- Preload models on startup with `hooks` ([#235](https://github.com/mostlygeek/llama-swap/pull/235))
|
- Preload models on startup with `hooks` ([#235](https://github.com/mostlygeek/llama-swap/pull/235))
|
||||||
|
- Apply filters to requests to control inference with `stripParams`, `setParams` and `setParamsByID`
|
||||||
|
|
||||||
### Web UI
|
### Web UI
|
||||||
|
|
||||||
@@ -94,8 +95,24 @@ llama-swap can be installed in multiple ways
|
|||||||
|
|
||||||
### Docker Install ([download images](https://github.com/mostlygeek/llama-swap/pkgs/container/llama-swap))
|
### Docker Install ([download images](https://github.com/mostlygeek/llama-swap/pkgs/container/llama-swap))
|
||||||
|
|
||||||
Nightly container images with llama-swap and llama-server are built for multiple platforms (cuda, vulkan, intel, etc.) including [non-root variants with improved security](docs/container-security.md).
|
Two types of container images are built nightly for llama-swap:
|
||||||
The stable-diffusion.cpp server is also included for the musa and vulkan platforms.
|
|
||||||
|
1. A unified container with llama-server, ik-llama-server, stable-diffusion.cpp, whisper.cpp and llama-swap built from source. This is only available for cuda and vulkan but has more capabilities. This one is recommended for use.
|
||||||
|
2. A legacy image that is based on llama.cpp's images and llama-swap copied into the container. Use this one if you prefer to stay close to llama.cpp's container images.
|
||||||
|
|
||||||
|
#### Unified container (Recommended)
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ docker pull ghcr.io/mostlygeek/llama-swap:unified-cuda
|
||||||
|
|
||||||
|
# run with a custom configuration and models directory
|
||||||
|
$ docker run -it --rm --runtime nvidia -p 9292:8080 \
|
||||||
|
-v /path/to/models:/models \
|
||||||
|
-v /path/to/custom/config.yaml:/etc/llama-swap/config/config.yaml \
|
||||||
|
ghcr.io/mostlygeek/llama-swap:unified-cuda
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Legacy container
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ docker pull ghcr.io/mostlygeek/llama-swap:cuda
|
$ docker pull ghcr.io/mostlygeek/llama-swap:cuda
|
||||||
@@ -105,14 +122,6 @@ $ docker run -it --rm --runtime nvidia -p 9292:8080 \
|
|||||||
-v /path/to/models:/models \
|
-v /path/to/models:/models \
|
||||||
-v /path/to/custom/config.yaml:/app/config.yaml \
|
-v /path/to/custom/config.yaml:/app/config.yaml \
|
||||||
ghcr.io/mostlygeek/llama-swap:cuda
|
ghcr.io/mostlygeek/llama-swap:cuda
|
||||||
|
|
||||||
# configuration hot reload supported with a
|
|
||||||
# directory volume mount
|
|
||||||
$ docker run -it --rm --runtime nvidia -p 9292:8080 \
|
|
||||||
-v /path/to/models:/models \
|
|
||||||
-v /path/to/custom/config.yaml:/app/config.yaml \
|
|
||||||
-v /path/to/config:/config \
|
|
||||||
ghcr.io/mostlygeek/llama-swap:cuda -config /config/config.yaml -watch-config
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@@ -268,6 +277,6 @@ For Python based inference servers like vllm or tabbyAPI it is recommended to ru
|
|||||||
## Star History
|
## Star History
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> ⭐️ Star this project to help others discover it!
|
> Thank you to everyone who has given this project a ⭐️!
|
||||||
|
|
||||||
[](https://www.star-history.com/#mostlygeek/llama-swap&Date)
|
[](https://www.star-history.com/#mostlygeek/llama-swap&Date)
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *stream {
|
if *stream {
|
||||||
m, _ := perf.New(config.PerformanceConfig{Enable: true, Every: every}, l)
|
m, _ := perf.New(config.PerformanceConfig{Every: every}, l)
|
||||||
m.Start()
|
m.Start()
|
||||||
defer m.Stop()
|
defer m.Stop()
|
||||||
sysCh, gpuCh, unsub := m.Subscribe()
|
sysCh, gpuCh, unsub := m.Subscribe()
|
||||||
|
|||||||
+4
-16
@@ -145,33 +145,21 @@
|
|||||||
"performance": {
|
"performance": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"enable": {
|
"disabled": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true,
|
"default": false,
|
||||||
"description": "Enable or disable system performance monitoring."
|
"description": "Disable system performance monitoring."
|
||||||
},
|
},
|
||||||
"every": {
|
"every": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^[-+]?(\\d+(\\.\\d+)?(ns|us|ms|s|m|h))+$",
|
"pattern": "^[-+]?(\\d+(\\.\\d+)?(ns|us|ms|s|m|h))+$",
|
||||||
"default": "15s",
|
"default": "15s",
|
||||||
"description": "Delay between polling for new performance statistics. Minimum duration is 1s. Lower values use more RAM as stats are kept in memory."
|
"description": "Delay between polling for new performance statistics. Minimum duration is 1s. Lower values use more RAM as stats are kept in memory."
|
||||||
},
|
|
||||||
"maxAge": {
|
|
||||||
"type": "string",
|
|
||||||
"pattern": "^[-+]?(\\d+(\\.\\d+)?(ns|us|ms|s|m|h))+$",
|
|
||||||
"default": "1h",
|
|
||||||
"description": "Maximum age of performance statistics before they are eligible for garbage collection."
|
|
||||||
},
|
|
||||||
"gc": {
|
|
||||||
"type": "string",
|
|
||||||
"pattern": "^[-+]?(\\d+(\\.\\d+)?(ns|us|ms|s|m|h))+$",
|
|
||||||
"default": "5m",
|
|
||||||
"description": "Garbage collection frequency for clearing old performance statistics."
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"default": {},
|
"default": {},
|
||||||
"description": "Configuration for system monitoring statistics. Timing values are duration strings like 1s, 1h30m, 90m, 2h10s."
|
"description": "Configuration for CPU, RAM and GPU monitoring statistics."
|
||||||
},
|
},
|
||||||
"startPort": {
|
"startPort": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
|||||||
+9
-19
@@ -58,25 +58,15 @@ captureBuffer: 15
|
|||||||
# performance: configuration for system monitoring statistics
|
# performance: configuration for system monitoring statistics
|
||||||
# - timing values are duration strings like 1s, 1h30m, 90m, 2h10s, etc.
|
# - timing values are duration strings like 1s, 1h30m, 90m, 2h10s, etc.
|
||||||
performance:
|
performance:
|
||||||
# enabled: boolean
|
# disabled: boolean
|
||||||
# - default: true
|
# - default: false
|
||||||
enable: true
|
disabled: false
|
||||||
|
|
||||||
# every: delay between polling for new performance statistics
|
# every: delay between polling for new performance statistics
|
||||||
# - default: 15s
|
# - default: 5s
|
||||||
# - minimum duration 1s
|
# - minimum duration 5s
|
||||||
# - note: setting this very low will use up more RAM as stats are kept in memory.
|
|
||||||
every: 15s
|
every: 15s
|
||||||
|
|
||||||
# maxAge: maximum age of a performance statistics before it is eligible for garbage collection
|
|
||||||
# - default: 1h
|
|
||||||
maxAge: 12h
|
|
||||||
|
|
||||||
# gc: garbage collection frequency in seconds
|
|
||||||
# - how many seconds the garbage collector runs to clear old stats
|
|
||||||
# - default 5m
|
|
||||||
gc: 5m
|
|
||||||
|
|
||||||
# startPort: sets the starting port number for the automatic ${PORT} macro.
|
# startPort: sets the starting port number for the automatic ${PORT} macro.
|
||||||
# - optional, default: 5800
|
# - optional, default: 5800
|
||||||
# - the ${PORT} macro can be used in model.cmd and model.proxy settings
|
# - the ${PORT} macro can be used in model.cmd and model.proxy settings
|
||||||
@@ -118,8 +108,7 @@ globalTTL: 0
|
|||||||
macros:
|
macros:
|
||||||
# Example of a multi-line macro
|
# Example of a multi-line macro
|
||||||
"latest-llama": >
|
"latest-llama": >
|
||||||
/path/to/llama-server/llama-server-ec9e0301
|
/path/to/llama-server/llama-server-ec9e0301 --port ${PORT}
|
||||||
--port ${PORT}
|
|
||||||
|
|
||||||
"default_ctx": 4096
|
"default_ctx": 4096
|
||||||
|
|
||||||
@@ -279,7 +268,8 @@ models:
|
|||||||
|
|
||||||
# the ${temp} macro will remain a float
|
# the ${temp} macro will remain a float
|
||||||
temperature: ${temp}
|
temperature: ${temp}
|
||||||
note: "The ${MODEL_ID} is running on port ${PORT} temp=${temp}, context=${default_ctx}"
|
note: "The ${MODEL_ID} is running on port ${PORT} temp=${temp},
|
||||||
|
context=${default_ctx}"
|
||||||
|
|
||||||
a_list:
|
a_list:
|
||||||
- 1
|
- 1
|
||||||
@@ -291,7 +281,7 @@ models:
|
|||||||
b: 2
|
b: 2
|
||||||
# objects can contain complex types with macro substitution
|
# objects can contain complex types with macro substitution
|
||||||
# becomes: c: [0.7, false, "model: llama"]
|
# becomes: c: [0.7, false, "model: llama"]
|
||||||
c: ["${temp}", false, "model: ${MODEL_ID}"]
|
c: [ "${temp}", false, "model: ${MODEL_ID}" ]
|
||||||
|
|
||||||
# concurrencyLimit: overrides the allowed number of active parallel requests to a model
|
# concurrencyLimit: overrides the allowed number of active parallel requests to a model
|
||||||
# - optional, default: 0
|
# - optional, default: 0
|
||||||
|
|||||||
+15
-3
@@ -146,6 +146,18 @@ metricsMaxInMemory: 1000
|
|||||||
# - set to 0 to disable
|
# - set to 0 to disable
|
||||||
captureBuffer: 15
|
captureBuffer: 15
|
||||||
|
|
||||||
|
# performance: configuration for system monitoring statistics
|
||||||
|
# - timing values are duration strings like 1s, 1h30m, 90m, 2h10s, etc.
|
||||||
|
performance:
|
||||||
|
# disabled: boolean
|
||||||
|
# - default: false
|
||||||
|
enable: true
|
||||||
|
|
||||||
|
# every: delay between polling for new performance statistics
|
||||||
|
# - default: 5s
|
||||||
|
# - minimum duration 5s
|
||||||
|
every: 5s
|
||||||
|
|
||||||
# startPort: sets the starting port number for the automatic ${PORT} macro.
|
# startPort: sets the starting port number for the automatic ${PORT} macro.
|
||||||
# - optional, default: 5800
|
# - optional, default: 5800
|
||||||
# - the ${PORT} macro can be used in model.cmd and model.proxy settings
|
# - the ${PORT} macro can be used in model.cmd and model.proxy settings
|
||||||
@@ -187,8 +199,7 @@ globalTTL: 0
|
|||||||
macros:
|
macros:
|
||||||
# Example of a multi-line macro
|
# Example of a multi-line macro
|
||||||
"latest-llama": >
|
"latest-llama": >
|
||||||
/path/to/llama-server/llama-server-ec9e0301
|
/path/to/llama-server/llama-server-ec9e0301 --port ${PORT}
|
||||||
--port ${PORT}
|
|
||||||
|
|
||||||
"default_ctx": 4096
|
"default_ctx": 4096
|
||||||
|
|
||||||
@@ -348,7 +359,8 @@ models:
|
|||||||
|
|
||||||
# the ${temp} macro will remain a float
|
# the ${temp} macro will remain a float
|
||||||
temperature: ${temp}
|
temperature: ${temp}
|
||||||
note: "The ${MODEL_ID} is running on port ${PORT} temp=${temp}, context=${default_ctx}"
|
note: "The ${MODEL_ID} is running on port ${PORT} temp=${temp},
|
||||||
|
context=${default_ctx}"
|
||||||
|
|
||||||
a_list:
|
a_list:
|
||||||
- 1
|
- 1
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ type Monitor struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ringCapacity(c config.PerformanceConfig) int {
|
func ringCapacity(c config.PerformanceConfig) int {
|
||||||
n := int(c.MaxAge / c.Every)
|
n := int(time.Hour / c.Every)
|
||||||
if n < 1 {
|
if n < 1 {
|
||||||
n = 1
|
n = 1
|
||||||
}
|
}
|
||||||
@@ -43,12 +43,6 @@ func New(c config.PerformanceConfig, logger *logmon.Monitor) (*Monitor, error) {
|
|||||||
if c.Every < 100*time.Millisecond {
|
if c.Every < 100*time.Millisecond {
|
||||||
c.Every = 100 * time.Millisecond
|
c.Every = 100 * time.Millisecond
|
||||||
}
|
}
|
||||||
if c.GC < 1*time.Second {
|
|
||||||
c.GC = 1 * time.Second
|
|
||||||
}
|
|
||||||
if c.MaxAge < 1*time.Minute {
|
|
||||||
c.MaxAge = 1 * time.Minute
|
|
||||||
}
|
|
||||||
|
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
return nil, errors.New("logger is required")
|
return nil, errors.New("logger is required")
|
||||||
@@ -92,7 +86,9 @@ func (m *Monitor) UpdateConfig(newConf config.PerformanceConfig) {
|
|||||||
m.sysRing = ring.NewBuffer[SysStat](capacity)
|
m.sysRing = ring.NewBuffer[SysStat](capacity)
|
||||||
m.gpuRing = ring.NewBuffer[[]GpuStat](capacity)
|
m.gpuRing = ring.NewBuffer[[]GpuStat](capacity)
|
||||||
m.mutex.Unlock()
|
m.mutex.Unlock()
|
||||||
m.Start()
|
if !newConf.Disabled {
|
||||||
|
m.Start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe returns channels to listen to system and GPU stats.
|
// Subscribe returns channels to listen to system and GPU stats.
|
||||||
|
|||||||
@@ -24,26 +24,19 @@ func TestNew_DefaultConfig(t *testing.T) {
|
|||||||
require.NotNil(t, m)
|
require.NotNil(t, m)
|
||||||
|
|
||||||
assert.Equal(t, 100*time.Millisecond, m.conf.Every)
|
assert.Equal(t, 100*time.Millisecond, m.conf.Every)
|
||||||
assert.Equal(t, 1*time.Second, m.conf.GC)
|
|
||||||
assert.Equal(t, 1*time.Minute, m.conf.MaxAge)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNew_CustomConfig(t *testing.T) {
|
func TestNew_CustomConfig(t *testing.T) {
|
||||||
logger := newTestLogger()
|
logger := newTestLogger()
|
||||||
|
|
||||||
cfg := config.PerformanceConfig{
|
cfg := config.PerformanceConfig{
|
||||||
Enable: true,
|
Every: 500 * time.Millisecond,
|
||||||
Every: 500 * time.Millisecond,
|
|
||||||
GC: 5 * time.Second,
|
|
||||||
MaxAge: 10 * time.Minute,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := New(cfg, logger)
|
m, err := New(cfg, logger)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 500*time.Millisecond, m.conf.Every)
|
assert.Equal(t, 500*time.Millisecond, m.conf.Every)
|
||||||
assert.Equal(t, 5*time.Second, m.conf.GC)
|
|
||||||
assert.Equal(t, 10*time.Minute, m.conf.MaxAge)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNew_NilLogger(t *testing.T) {
|
func TestNew_NilLogger(t *testing.T) {
|
||||||
@@ -56,18 +49,13 @@ func TestNew_BelowMinimumConfig(t *testing.T) {
|
|||||||
logger := newTestLogger()
|
logger := newTestLogger()
|
||||||
|
|
||||||
cfg := config.PerformanceConfig{
|
cfg := config.PerformanceConfig{
|
||||||
Enable: true,
|
Every: 1 * time.Millisecond,
|
||||||
Every: 1 * time.Millisecond,
|
|
||||||
GC: 100 * time.Millisecond,
|
|
||||||
MaxAge: 1 * time.Second,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := New(cfg, logger)
|
m, err := New(cfg, logger)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 100*time.Millisecond, m.conf.Every)
|
assert.Equal(t, 100*time.Millisecond, m.conf.Every)
|
||||||
assert.Equal(t, 1*time.Second, m.conf.GC)
|
|
||||||
assert.Equal(t, 1*time.Minute, m.conf.MaxAge)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSubscribe_ReturnsChannels(t *testing.T) {
|
func TestSubscribe_ReturnsChannels(t *testing.T) {
|
||||||
|
|||||||
+17
-8
@@ -93,12 +93,17 @@ func main() {
|
|||||||
listenStr = &defaultPort
|
listenStr = &defaultPort
|
||||||
}
|
}
|
||||||
|
|
||||||
mon, err := perf.New(conf.Performance, mainLogger)
|
var mon *perf.Monitor
|
||||||
if err != nil {
|
if !conf.Performance.Disabled {
|
||||||
mainLogger.Errorf("failed to create monitor: %s", err.Error())
|
mon, err = perf.New(conf.Performance, mainLogger)
|
||||||
os.Exit(1)
|
if err != nil {
|
||||||
|
mainLogger.Errorf("failed to create monitor: %s", err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
mon.Start()
|
||||||
|
} else {
|
||||||
|
mainLogger.Info("performance monitoring is disabled")
|
||||||
}
|
}
|
||||||
mon.Start()
|
|
||||||
|
|
||||||
// Setup channels for server management
|
// Setup channels for server management
|
||||||
exitChan := make(chan struct{})
|
exitChan := make(chan struct{})
|
||||||
@@ -108,7 +113,7 @@ func main() {
|
|||||||
// Context that bounds the lifetime of background watcher goroutines.
|
// Context that bounds the lifetime of background watcher goroutines.
|
||||||
watcherCtx, watcherCancel := context.WithCancel(context.Background())
|
watcherCtx, watcherCancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
// Create server with initial handler
|
// Create server with initial handlergit
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Addr: *listenStr,
|
Addr: *listenStr,
|
||||||
}
|
}
|
||||||
@@ -140,7 +145,9 @@ func main() {
|
|||||||
|
|
||||||
mainLogger.Debug("Configuration Changed")
|
mainLogger.Debug("Configuration Changed")
|
||||||
currentPM.Shutdown()
|
currentPM.Shutdown()
|
||||||
mon.UpdateConfig(conf.Performance)
|
if mon != nil {
|
||||||
|
mon.UpdateConfig(conf.Performance)
|
||||||
|
}
|
||||||
newPM := proxy.New(conf)
|
newPM := proxy.New(conf)
|
||||||
newPM.SetVersion(date, commit, version)
|
newPM.SetVersion(date, commit, version)
|
||||||
newPM.SetPerfMonitor(mon)
|
newPM.SetPerfMonitor(mon)
|
||||||
@@ -197,7 +204,9 @@ func main() {
|
|||||||
reloadProxyManager()
|
reloadProxyManager()
|
||||||
case syscall.SIGINT, syscall.SIGTERM:
|
case syscall.SIGINT, syscall.SIGTERM:
|
||||||
mainLogger.Debugf("Received signal %v, shutting down...", sig)
|
mainLogger.Debugf("Received signal %v, shutting down...", sig)
|
||||||
mon.Stop()
|
if mon != nil {
|
||||||
|
mon.Stop()
|
||||||
|
}
|
||||||
watcherCancel()
|
watcherCancel()
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|||||||
@@ -223,11 +223,8 @@ func LoadConfigFromReader(r io.Reader) (Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply defaults for performance config when section is missing
|
// Apply defaults for performance config when section is missing
|
||||||
if !config.Performance.Enable && config.Performance.Every == 0 && config.Performance.MaxAge == 0 && config.Performance.GC == 0 {
|
if config.Performance.Every == 0 {
|
||||||
config.Performance.Enable = true
|
config.Performance.Every = 5 * time.Second
|
||||||
config.Performance.Every = 15 * time.Second
|
|
||||||
config.Performance.MaxAge = 1 * time.Hour
|
|
||||||
config.Performance.GC = 5 * time.Minute
|
|
||||||
}
|
}
|
||||||
if err = config.Performance.Validate(); err != nil {
|
if err = config.Performance.Validate(); err != nil {
|
||||||
return Config{}, fmt.Errorf("performance: %w", err)
|
return Config{}, fmt.Errorf("performance: %w", err)
|
||||||
|
|||||||
@@ -231,10 +231,7 @@ groups:
|
|||||||
MetricsMaxInMemory: 1000,
|
MetricsMaxInMemory: 1000,
|
||||||
CaptureBuffer: 5,
|
CaptureBuffer: 5,
|
||||||
Performance: PerformanceConfig{
|
Performance: PerformanceConfig{
|
||||||
Enable: true,
|
Every: 5 * time.Second,
|
||||||
Every: 15 * time.Second,
|
|
||||||
MaxAge: 1 * time.Hour,
|
|
||||||
GC: 5 * time.Minute,
|
|
||||||
},
|
},
|
||||||
Profiles: map[string][]string{
|
Profiles: map[string][]string{
|
||||||
"test": {"model1", "model2"},
|
"test": {"model1", "model2"},
|
||||||
|
|||||||
@@ -220,10 +220,7 @@ groups:
|
|||||||
MetricsMaxInMemory: 1000,
|
MetricsMaxInMemory: 1000,
|
||||||
CaptureBuffer: 5,
|
CaptureBuffer: 5,
|
||||||
Performance: PerformanceConfig{
|
Performance: PerformanceConfig{
|
||||||
Enable: true,
|
Every: 5 * time.Second,
|
||||||
Every: 15 * time.Second,
|
|
||||||
MaxAge: 1 * time.Hour,
|
|
||||||
GC: 5 * time.Minute,
|
|
||||||
},
|
},
|
||||||
Profiles: map[string][]string{
|
Profiles: map[string][]string{
|
||||||
"test": {"model1", "model2"},
|
"test": {"model1", "model2"},
|
||||||
|
|||||||
@@ -7,19 +7,14 @@ import (
|
|||||||
|
|
||||||
// PerformanceConfig holds configuration for system performance monitoring
|
// PerformanceConfig holds configuration for system performance monitoring
|
||||||
type PerformanceConfig struct {
|
type PerformanceConfig struct {
|
||||||
Enable bool `yaml:"enable"`
|
Disabled bool `yaml:"disabled"`
|
||||||
Every time.Duration `yaml:"every"`
|
Every time.Duration `yaml:"every"`
|
||||||
MaxAge time.Duration `yaml:"maxAge"`
|
|
||||||
GC time.Duration `yaml:"gc"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PerformanceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
func (p *PerformanceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
type rawPerformanceConfig PerformanceConfig
|
type rawPerformanceConfig PerformanceConfig
|
||||||
defaults := rawPerformanceConfig{
|
defaults := rawPerformanceConfig{
|
||||||
Enable: true,
|
Every: 5 * time.Second,
|
||||||
Every: 15 * time.Second,
|
|
||||||
MaxAge: 1 * time.Hour,
|
|
||||||
GC: 5 * time.Minute,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := unmarshal(&defaults); err != nil {
|
if err := unmarshal(&defaults); err != nil {
|
||||||
@@ -32,14 +27,8 @@ func (p *PerformanceConfig) UnmarshalYAML(unmarshal func(interface{}) error) err
|
|||||||
|
|
||||||
// Validate checks the PerformanceConfig values and returns an error if invalid
|
// Validate checks the PerformanceConfig values and returns an error if invalid
|
||||||
func (p *PerformanceConfig) Validate() error {
|
func (p *PerformanceConfig) Validate() error {
|
||||||
if p.Every < time.Second {
|
if p.Every < 5*time.Second {
|
||||||
return fmt.Errorf("every must be at least 1s, got %v", p.Every)
|
return fmt.Errorf("every must be at least 5s, got %v", p.Every)
|
||||||
}
|
|
||||||
if p.MaxAge <= 0 {
|
|
||||||
return fmt.Errorf("maxAge must be greater than 0, got %v", p.MaxAge)
|
|
||||||
}
|
|
||||||
if p.GC <= 0 {
|
|
||||||
return fmt.Errorf("gc must be greater than 0, got %v", p.GC)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,8 @@ models:
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// When performance section is missing, defaults should be applied
|
// When performance section is missing, defaults should be applied
|
||||||
assert.True(t, config.Performance.Enable)
|
assert.False(t, config.Performance.Disabled)
|
||||||
assert.Equal(t, 15*time.Second, config.Performance.Every)
|
assert.Equal(t, 5*time.Second, config.Performance.Every)
|
||||||
assert.Equal(t, 1*time.Hour, config.Performance.MaxAge)
|
|
||||||
assert.Equal(t, 5*time.Minute, config.Performance.GC)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPerformanceConfig_CustomValues(t *testing.T) {
|
func TestPerformanceConfig_CustomValues(t *testing.T) {
|
||||||
@@ -29,8 +27,6 @@ func TestPerformanceConfig_CustomValues(t *testing.T) {
|
|||||||
performance:
|
performance:
|
||||||
enable: true
|
enable: true
|
||||||
every: 30s
|
every: 30s
|
||||||
maxAge: 12h
|
|
||||||
gc: 10m
|
|
||||||
models:
|
models:
|
||||||
model1:
|
model1:
|
||||||
cmd: path/to/cmd --port ${PORT}
|
cmd: path/to/cmd --port ${PORT}
|
||||||
@@ -38,16 +34,14 @@ models:
|
|||||||
config, err := LoadConfigFromReader(strings.NewReader(content))
|
config, err := LoadConfigFromReader(strings.NewReader(content))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.True(t, config.Performance.Enable)
|
assert.False(t, config.Performance.Disabled)
|
||||||
assert.Equal(t, 30*time.Second, config.Performance.Every)
|
assert.Equal(t, 30*time.Second, config.Performance.Every)
|
||||||
assert.Equal(t, 12*time.Hour, config.Performance.MaxAge)
|
|
||||||
assert.Equal(t, 10*time.Minute, config.Performance.GC)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPerformanceConfig_Disabled(t *testing.T) {
|
func TestPerformanceConfig_Disabled(t *testing.T) {
|
||||||
content := `
|
content := `
|
||||||
performance:
|
performance:
|
||||||
enable: false
|
disabled: true
|
||||||
models:
|
models:
|
||||||
model1:
|
model1:
|
||||||
cmd: path/to/cmd --port ${PORT}
|
cmd: path/to/cmd --port ${PORT}
|
||||||
@@ -55,18 +49,15 @@ models:
|
|||||||
config, err := LoadConfigFromReader(strings.NewReader(content))
|
config, err := LoadConfigFromReader(strings.NewReader(content))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.False(t, config.Performance.Enable)
|
assert.True(t, config.Performance.Disabled)
|
||||||
// Duration defaults should still apply
|
// Duration defaults should still apply
|
||||||
assert.Equal(t, 15*time.Second, config.Performance.Every)
|
assert.Equal(t, 5*time.Second, config.Performance.Every)
|
||||||
assert.Equal(t, 1*time.Hour, config.Performance.MaxAge)
|
|
||||||
assert.Equal(t, 5*time.Minute, config.Performance.GC)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPerformanceConfig_PartialValues(t *testing.T) {
|
func TestPerformanceConfig_PartialValues(t *testing.T) {
|
||||||
content := `
|
content := `
|
||||||
performance:
|
performance:
|
||||||
every: 10s
|
every: 10s
|
||||||
maxAge: 6h
|
|
||||||
models:
|
models:
|
||||||
model1:
|
model1:
|
||||||
cmd: path/to/cmd --port ${PORT}
|
cmd: path/to/cmd --port ${PORT}
|
||||||
@@ -75,58 +66,27 @@ models:
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// enable should default to true
|
// enable should default to true
|
||||||
assert.True(t, config.Performance.Enable)
|
assert.False(t, config.Performance.Disabled)
|
||||||
assert.Equal(t, 10*time.Second, config.Performance.Every)
|
assert.Equal(t, 10*time.Second, config.Performance.Every)
|
||||||
assert.Equal(t, 6*time.Hour, config.Performance.MaxAge)
|
|
||||||
// gc should use default
|
|
||||||
assert.Equal(t, 5*time.Minute, config.Performance.GC)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPerformanceConfig_InvalidEvery(t *testing.T) {
|
func TestPerformanceConfig_InvalidEvery(t *testing.T) {
|
||||||
content := `
|
content := `
|
||||||
performance:
|
performance:
|
||||||
every: 500ms
|
every: 4s
|
||||||
models:
|
models:
|
||||||
model1:
|
model1:
|
||||||
cmd: path/to/cmd --port ${PORT}
|
cmd: path/to/cmd --port ${PORT}
|
||||||
`
|
`
|
||||||
_, err := LoadConfigFromReader(strings.NewReader(content))
|
_, err := LoadConfigFromReader(strings.NewReader(content))
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.Error(), "every must be at least 1s")
|
assert.Contains(t, err.Error(), "every must be at least 5s")
|
||||||
}
|
|
||||||
|
|
||||||
func TestPerformanceConfig_InvalidMaxAge(t *testing.T) {
|
|
||||||
content := `
|
|
||||||
performance:
|
|
||||||
maxAge: 0s
|
|
||||||
models:
|
|
||||||
model1:
|
|
||||||
cmd: path/to/cmd --port ${PORT}
|
|
||||||
`
|
|
||||||
_, err := LoadConfigFromReader(strings.NewReader(content))
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Contains(t, err.Error(), "maxAge must be greater than 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPerformanceConfig_InvalidGC(t *testing.T) {
|
|
||||||
content := `
|
|
||||||
performance:
|
|
||||||
gc: 0s
|
|
||||||
models:
|
|
||||||
model1:
|
|
||||||
cmd: path/to/cmd --port ${PORT}
|
|
||||||
`
|
|
||||||
_, err := LoadConfigFromReader(strings.NewReader(content))
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Contains(t, err.Error(), "gc must be greater than 0")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPerformanceConfig_ComplexDurations(t *testing.T) {
|
func TestPerformanceConfig_ComplexDurations(t *testing.T) {
|
||||||
content := `
|
content := `
|
||||||
performance:
|
performance:
|
||||||
every: 1m30s
|
every: 1m30s
|
||||||
maxAge: 2h10m
|
|
||||||
gc: 1m
|
|
||||||
models:
|
models:
|
||||||
model1:
|
model1:
|
||||||
cmd: path/to/cmd --port ${PORT}
|
cmd: path/to/cmd --port ${PORT}
|
||||||
@@ -135,6 +95,4 @@ models:
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 90*time.Second, config.Performance.Every)
|
assert.Equal(t, 90*time.Second, config.Performance.Every)
|
||||||
assert.Equal(t, (2*time.Hour)+(10*time.Minute), config.Performance.MaxAge)
|
|
||||||
assert.Equal(t, 1*time.Minute, config.Performance.GC)
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user