Version 1 checkpoint
This commit is contained in:
@@ -61,7 +61,7 @@ For non-Ubuntu WSL distros, install the CUDA toolkit manually before running the
|
|||||||
- The host-side install path assumes modern local tooling, but TransformerLab itself is provisioned from a pinned classic single-user layout.
|
- The host-side install path assumes modern local tooling, but TransformerLab itself is provisioned from a pinned classic single-user layout.
|
||||||
- TransformerLab is intentionally pinned to the older single-user `v0.28.2` release because newer upstream releases changed the project structure and behavior in ways that break this courseware.
|
- TransformerLab is intentionally pinned to the older single-user `v0.28.2` release because newer upstream releases changed the project structure and behavior in ways that break this courseware.
|
||||||
- This project does not rely on TransformerLab's upstream `install.sh`; the Ansible role provisions the pinned release directly so web assets, env layout, and runtime behavior stay reproducible.
|
- This project does not rely on TransformerLab's upstream `install.sh`; the Ansible role provisions the pinned release directly so web assets, env layout, and runtime behavior stay reproducible.
|
||||||
- The scripts do not patch TransformerLab plugins or preserve the VM's special-case fixes.
|
- The courseware repairs installed TransformerLab Fastchat plugin manifests so Fastchat-gated features such as Model Architecture and Visualize Logprobs stay available on pinned installs.
|
||||||
- No Ollama models are pulled during `./labctl up`; students pull models manually as part of the courseware.
|
- No Ollama models are pulled during `./labctl up`; students pull models manually as part of the courseware.
|
||||||
- WhiteRabbitNeo GGUFs are no longer pulled during `./labctl up`. After base setup, run `state/lab2/download_whiterabbitneo-gguf.sh` to fetch only the `BF16`, `Q8_0`, `Q4_K_M`, and `Q2_K` files from `bartowski/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF` and register local Ollama models `WhiteRabbitNeo`, `WhiteRabbitNeo-BF16`, `WhiteRabbitNeo-Q8`, `WhiteRabbitNeo-Q4`, and `WhiteRabbitNeo-Q2`.
|
- WhiteRabbitNeo GGUFs are no longer pulled during `./labctl up`. After base setup, run `state/lab2/download_whiterabbitneo-gguf.sh` to fetch only the `BF16`, `Q8_0`, `Q4_K_M`, and `Q2_K` files from `bartowski/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF` and register local Ollama models `WhiteRabbitNeo`, `WhiteRabbitNeo-BF16`, `WhiteRabbitNeo-Q8`, `WhiteRabbitNeo-Q4`, and `WhiteRabbitNeo-Q2`.
|
||||||
- TransformerLab and Unsloth homes are redirected into this project's `state/` tree via symlinks.
|
- TransformerLab and Unsloth homes are redirected into this project's `state/` tree via symlinks.
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
[defaults]
|
[defaults]
|
||||||
inventory = inventory/localhost.yml
|
inventory = inventory
|
||||||
roles_path = roles
|
roles_path = roles
|
||||||
host_key_checking = False
|
host_key_checking = False
|
||||||
interpreter_python = auto_silent
|
interpreter_python = auto_silent
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ courseware_apps_dir: "{{ courseware_state_dir }}/apps"
|
|||||||
courseware_downloads_dir: "{{ courseware_state_dir }}/downloads"
|
courseware_downloads_dir: "{{ courseware_state_dir }}/downloads"
|
||||||
courseware_lab2_dir: "{{ courseware_state_dir }}/lab2"
|
courseware_lab2_dir: "{{ courseware_state_dir }}/lab2"
|
||||||
courseware_lab6_dir: "{{ courseware_state_dir }}/lab6"
|
courseware_lab6_dir: "{{ courseware_state_dir }}/lab6"
|
||||||
courseware_transformerlab_home: "{{ courseware_state_dir }}/transformerlab-home"
|
courseware_transformerlab_legacy_home: "{{ courseware_state_dir }}/transformerlab-home"
|
||||||
|
courseware_safe_homes_dir: "{{ lookup('env', 'HOME') }}/.local/share/local-lab-deployment"
|
||||||
|
courseware_transformerlab_home: "{{ (courseware_safe_homes_dir ~ '/transformerlab-home') if ' ' in courseware_root else courseware_transformerlab_legacy_home }}"
|
||||||
courseware_unsloth_home: "{{ courseware_state_dir }}/unsloth-home"
|
courseware_unsloth_home: "{{ courseware_state_dir }}/unsloth-home"
|
||||||
courseware_ollama_models_dir: "{{ courseware_models_dir }}/ollama"
|
courseware_ollama_models_dir: "{{ courseware_models_dir }}/ollama"
|
||||||
courseware_node_runtime_dir: "{{ courseware_tools_dir }}/node-runtime"
|
courseware_node_runtime_dir: "{{ courseware_tools_dir }}/node-runtime"
|
||||||
@@ -32,8 +34,30 @@ courseware_ports:
|
|||||||
promptfoo: 15500
|
promptfoo: 15500
|
||||||
wiki: 80
|
wiki: 80
|
||||||
|
|
||||||
|
courseware_transformerlab_install_mode: "single-user-pinned"
|
||||||
courseware_transformerlab_version: "v0.28.2"
|
courseware_transformerlab_version: "v0.28.2"
|
||||||
courseware_transformerlab_version_dir: "{{ courseware_transformerlab_version | regex_replace('^v', '') }}"
|
courseware_transformerlab_version_dir: "{{ courseware_transformerlab_version | regex_replace('^v', '') }}"
|
||||||
|
courseware_transformerlab_source_archive: "{{ courseware_downloads_dir }}/transformerlab-app-{{ courseware_transformerlab_version_dir }}.tar.gz"
|
||||||
|
courseware_transformerlab_web_archive: "{{ courseware_downloads_dir }}/transformerlab-web-{{ courseware_transformerlab_version_dir }}.tar.gz"
|
||||||
|
courseware_transformerlab_miniforge_installer: "{{ courseware_downloads_dir }}/transformerlab-miniforge-installer.sh"
|
||||||
|
courseware_transformerlab_default_user_email: "student@zuccaro.me"
|
||||||
|
courseware_transformerlab_default_user_password: "student"
|
||||||
|
courseware_transformerlab_default_user_first_name: "Student"
|
||||||
|
courseware_transformerlab_default_user_last_name: ""
|
||||||
|
courseware_transformerlab_required_loader_plugins:
|
||||||
|
- "fastchat_server"
|
||||||
|
courseware_transformerlab_required_supports_fastchat:
|
||||||
|
- "chat"
|
||||||
|
- "completion"
|
||||||
|
- "visualize_model"
|
||||||
|
- "model_layers"
|
||||||
|
- "rag"
|
||||||
|
- "tools"
|
||||||
|
- "template"
|
||||||
|
- "embeddings"
|
||||||
|
- "tokenize"
|
||||||
|
- "logprobs"
|
||||||
|
- "batched"
|
||||||
courseware_llama_cpp_commit: "51fa458a92d6a3f305f8fd76fc8f702e3e87ddb5"
|
courseware_llama_cpp_commit: "51fa458a92d6a3f305f8fd76fc8f702e3e87ddb5"
|
||||||
courseware_chunkviz_commit: "a891eacafda1f28a12373ad3b00102e68f07c57f"
|
courseware_chunkviz_commit: "a891eacafda1f28a12373ad3b00102e68f07c57f"
|
||||||
courseware_promptfoo_version: "0.119.0"
|
courseware_promptfoo_version: "0.119.0"
|
||||||
|
|||||||
@@ -192,6 +192,19 @@
|
|||||||
or courseware_down_transformerlab_marker.stat.exists
|
or courseware_down_transformerlab_marker.stat.exists
|
||||||
failed_when: false
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Remove managed TransformerLab home
|
||||||
|
file:
|
||||||
|
path: "{{ courseware_transformerlab_home }}"
|
||||||
|
state: absent
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Remove legacy managed TransformerLab home
|
||||||
|
file:
|
||||||
|
path: "{{ courseware_transformerlab_legacy_home }}"
|
||||||
|
state: absent
|
||||||
|
when: courseware_transformerlab_legacy_home != courseware_transformerlab_home
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
- name: Remove managed Unsloth path
|
- name: Remove managed Unsloth path
|
||||||
file:
|
file:
|
||||||
path: "{{ ansible_env.HOME }}/.unsloth"
|
path: "{{ ansible_env.HOME }}/.unsloth"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
- "{{ courseware_apps_dir }}"
|
- "{{ courseware_apps_dir }}"
|
||||||
- "{{ courseware_downloads_dir }}"
|
- "{{ courseware_downloads_dir }}"
|
||||||
- "{{ courseware_lab2_dir }}"
|
- "{{ courseware_lab2_dir }}"
|
||||||
|
- "{{ courseware_safe_homes_dir }}"
|
||||||
- "{{ courseware_transformerlab_home }}"
|
- "{{ courseware_transformerlab_home }}"
|
||||||
- "{{ courseware_unsloth_home }}"
|
- "{{ courseware_unsloth_home }}"
|
||||||
- "{{ courseware_ollama_models_dir }}"
|
- "{{ courseware_ollama_models_dir }}"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
- name: Create Open WebUI virtual environment
|
- name: Create Open WebUI virtual environment
|
||||||
command:
|
command:
|
||||||
argv:
|
argv:
|
||||||
- "{{ courseware_python_bin }}"
|
- "{{ courseware_transformerlab_home }}/envs/transformerlab/bin/python"
|
||||||
- -m
|
- -m
|
||||||
- venv
|
- venv
|
||||||
- "{{ courseware_venvs_dir }}/open-webui"
|
- "{{ courseware_venvs_dir }}/open-webui"
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
- name: Create Embedding Atlas virtual environment
|
- name: Create Embedding Atlas virtual environment
|
||||||
command:
|
command:
|
||||||
argv:
|
argv:
|
||||||
- "{{ courseware_python_bin }}"
|
- "{{ courseware_transformerlab_home }}/envs/transformerlab/bin/python"
|
||||||
- -m
|
- -m
|
||||||
- venv
|
- venv
|
||||||
- "{{ courseware_venvs_dir }}/embedding-atlas"
|
- "{{ courseware_venvs_dir }}/embedding-atlas"
|
||||||
|
|||||||
@@ -1,139 +1,290 @@
|
|||||||
- name: Bootstrap TransformerLab release files
|
- name: Require pinned single-user TransformerLab install mode
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- courseware_transformerlab_install_mode == "single-user-pinned"
|
||||||
|
fail_msg: "This courseware only supports the pinned single-user TransformerLab install mode."
|
||||||
|
|
||||||
|
- name: Check pinned TransformerLab source tree
|
||||||
|
stat:
|
||||||
|
path: "{{ courseware_transformerlab_home }}/src/install.sh"
|
||||||
|
register: courseware_transformerlab_src_install
|
||||||
|
|
||||||
|
- name: Check pinned TransformerLab version file
|
||||||
|
stat:
|
||||||
|
path: "{{ courseware_transformerlab_home }}/src/LATEST_VERSION"
|
||||||
|
register: courseware_transformerlab_src_version
|
||||||
|
|
||||||
|
- name: Check pinned TransformerLab web app
|
||||||
|
stat:
|
||||||
|
path: "{{ courseware_transformerlab_home }}/webapp/index.html"
|
||||||
|
register: courseware_transformerlab_web_index
|
||||||
|
|
||||||
|
- name: Check pinned TransformerLab conda runtime
|
||||||
|
stat:
|
||||||
|
path: "{{ courseware_transformerlab_home }}/miniforge3/bin/conda"
|
||||||
|
register: courseware_transformerlab_conda
|
||||||
|
|
||||||
|
- name: Check pinned TransformerLab Python runtime
|
||||||
|
stat:
|
||||||
|
path: "{{ courseware_transformerlab_home }}/envs/transformerlab/bin/python"
|
||||||
|
register: courseware_transformerlab_env_python
|
||||||
|
|
||||||
|
- name: Check pinned TransformerLab install marker
|
||||||
|
stat:
|
||||||
|
path: "{{ courseware_transformerlab_home }}/.install_complete"
|
||||||
|
register: courseware_transformerlab_install_marker
|
||||||
|
|
||||||
|
- name: Read pinned TransformerLab version file
|
||||||
|
slurp:
|
||||||
|
src: "{{ courseware_transformerlab_home }}/src/LATEST_VERSION"
|
||||||
|
register: courseware_transformerlab_version_contents
|
||||||
|
when: courseware_transformerlab_src_version.stat.exists
|
||||||
|
|
||||||
|
- name: Determine pinned TransformerLab drift state
|
||||||
|
set_fact:
|
||||||
|
courseware_transformerlab_installed_version: "{{ (courseware_transformerlab_version_contents.content | b64decode | trim) if courseware_transformerlab_src_version.stat.exists else '' }}"
|
||||||
|
courseware_transformerlab_refresh_required: >-
|
||||||
|
{{
|
||||||
|
(not courseware_transformerlab_src_install.stat.exists) or
|
||||||
|
(not courseware_transformerlab_src_version.stat.exists) or
|
||||||
|
(not courseware_transformerlab_web_index.stat.exists) or
|
||||||
|
((courseware_transformerlab_version_contents.content | b64decode | trim) != courseware_transformerlab_version)
|
||||||
|
}}
|
||||||
|
courseware_transformerlab_env_refresh_required: >-
|
||||||
|
{{
|
||||||
|
(not courseware_transformerlab_conda.stat.exists) or
|
||||||
|
(not courseware_transformerlab_env_python.stat.exists) or
|
||||||
|
(not courseware_transformerlab_install_marker.stat.exists)
|
||||||
|
}}
|
||||||
|
|
||||||
|
- name: Download pinned TransformerLab source archive
|
||||||
|
get_url:
|
||||||
|
url: "https://github.com/transformerlab/transformerlab-app/archive/refs/tags/{{ courseware_transformerlab_version }}.tar.gz"
|
||||||
|
dest: "{{ courseware_transformerlab_source_archive }}"
|
||||||
|
mode: "0644"
|
||||||
|
when: courseware_transformerlab_refresh_required
|
||||||
|
|
||||||
|
- name: Download pinned TransformerLab web archive
|
||||||
|
get_url:
|
||||||
|
url: "https://github.com/transformerlab/transformerlab-app/releases/download/{{ courseware_transformerlab_version }}/transformerlab_web.tar.gz"
|
||||||
|
dest: "{{ courseware_transformerlab_web_archive }}"
|
||||||
|
mode: "0644"
|
||||||
|
when: courseware_transformerlab_refresh_required
|
||||||
|
|
||||||
|
- name: Refresh pinned TransformerLab release layout
|
||||||
shell: |
|
shell: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
cd "{{ courseware_transformerlab_home }}"
|
home_dir="{{ courseware_transformerlab_home }}"
|
||||||
curl -L "https://github.com/transformerlab/transformerlab-app/archive/refs/tags/{{ courseware_transformerlab_version }}.tar.gz" -o transformerlab.tar.gz
|
source_archive="{{ courseware_transformerlab_source_archive }}"
|
||||||
tar -xzf transformerlab.tar.gz
|
web_archive="{{ courseware_transformerlab_web_archive }}"
|
||||||
rm -f transformerlab.tar.gz
|
version="{{ courseware_transformerlab_version }}"
|
||||||
rm -rf src
|
version_dir="{{ courseware_transformerlab_version_dir }}"
|
||||||
mv "transformerlab-app-{{ courseware_transformerlab_version_dir }}/api" src
|
extract_dir="${home_dir}/transformerlab-app-${version_dir}"
|
||||||
echo "{{ courseware_transformerlab_version }}" > src/LATEST_VERSION
|
|
||||||
curl -L "https://github.com/transformerlab/transformerlab-app/releases/download/{{ courseware_transformerlab_version }}/transformerlab_web.tar.gz" -o transformerlab_web.tar.gz
|
|
||||||
rm -rf webapp
|
|
||||||
mkdir -p webapp
|
|
||||||
tar -xzf transformerlab_web.tar.gz -C webapp
|
|
||||||
rm -f transformerlab_web.tar.gz
|
|
||||||
args:
|
|
||||||
executable: /bin/bash
|
|
||||||
creates: "{{ courseware_transformerlab_home }}/src/install.sh"
|
|
||||||
|
|
||||||
- name: Add TransformerLab Miniforge Python path for space-safe bootstrap
|
rm -rf "${extract_dir}"
|
||||||
replace:
|
tar -xzf "${source_archive}" -C "${home_dir}"
|
||||||
path: "{{ courseware_transformerlab_home }}/src/install.sh"
|
|
||||||
regexp: 'CONDA_BIN=\$\{MINIFORGE_ROOT\}/bin/conda\n'
|
|
||||||
replace: |
|
|
||||||
CONDA_BIN=${MINIFORGE_ROOT}/bin/conda
|
|
||||||
CONDA_PYTHON_BIN=${MINIFORGE_ROOT}/bin/python
|
|
||||||
when: "' ' in courseware_transformerlab_home"
|
|
||||||
|
|
||||||
- name: Inject space-safe TransformerLab conda runner
|
if [ ! -d "${extract_dir}/api" ]; then
|
||||||
blockinfile:
|
echo "Pinned TransformerLab source archive did not contain api/." >&2
|
||||||
path: "{{ courseware_transformerlab_home }}/src/install.sh"
|
exit 1
|
||||||
insertbefore: '^check_conda\(\) \{$'
|
|
||||||
marker: '# {mark} courseware conda runner'
|
|
||||||
block: |
|
|
||||||
conda_direct_exec_works() {
|
|
||||||
"${CONDA_BIN}" --version >/dev/null 2>&1
|
|
||||||
}
|
|
||||||
|
|
||||||
run_conda() {
|
|
||||||
if conda_direct_exec_works; then
|
|
||||||
"${CONDA_BIN}" "$@"
|
|
||||||
else
|
|
||||||
"${CONDA_PYTHON_BIN}" "${CONDA_BIN}" "$@"
|
|
||||||
fi
|
fi
|
||||||
}
|
|
||||||
when: "' ' in courseware_transformerlab_home"
|
|
||||||
|
|
||||||
- name: Rewrite TransformerLab installer to use the space-safe conda runner
|
rm -rf "${home_dir}/src" "${home_dir}/lab-sdk" "${home_dir}/webapp"
|
||||||
replace:
|
mv "${extract_dir}/api" "${home_dir}/src"
|
||||||
path: "{{ courseware_transformerlab_home }}/src/install.sh"
|
|
||||||
regexp: 'eval "\$\(\$\{CONDA_BIN\} shell\.bash hook\)"'
|
|
||||||
replace: 'eval "$(run_conda shell.bash hook)"'
|
|
||||||
when: "' ' in courseware_transformerlab_home"
|
|
||||||
|
|
||||||
- name: Rewrite TransformerLab doctor output to use the space-safe conda runner
|
if [ -d "${extract_dir}/lab-sdk" ]; then
|
||||||
replace:
|
mv "${extract_dir}/lab-sdk" "${home_dir}/lab-sdk"
|
||||||
path: "{{ courseware_transformerlab_home }}/src/install.sh"
|
fi
|
||||||
regexp: '\$\(\$\{CONDA_BIN\} --version\)'
|
|
||||||
replace: '$(run_conda --version)'
|
|
||||||
when: "' ' in courseware_transformerlab_home"
|
|
||||||
|
|
||||||
- name: Install TransformerLab
|
printf '%s\n' "${version}" > "${home_dir}/src/LATEST_VERSION"
|
||||||
shell: |
|
|
||||||
set -euo pipefail
|
mkdir -p "${home_dir}/webapp"
|
||||||
./src/install.sh 2>&1 | tee "{{ courseware_logs_dir }}/transformerlab_install.log"
|
tar -xzf "${web_archive}" -C "${home_dir}/webapp"
|
||||||
touch "{{ courseware_transformerlab_home }}/.courseware-managed"
|
if [ -d "${home_dir}/webapp/transformerlab_web" ]; then
|
||||||
|
shopt -s dotglob nullglob
|
||||||
|
mv "${home_dir}/webapp/transformerlab_web/"* "${home_dir}/webapp/"
|
||||||
|
rmdir "${home_dir}/webapp/transformerlab_web"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf "${extract_dir}"
|
||||||
|
rm -f "${home_dir}/.install_complete"
|
||||||
args:
|
args:
|
||||||
executable: /bin/bash
|
executable: /bin/bash
|
||||||
chdir: "{{ courseware_transformerlab_home }}"
|
when: courseware_transformerlab_refresh_required
|
||||||
creates: "{{ courseware_transformerlab_home }}/miniforge3/bin/conda"
|
|
||||||
|
|
||||||
- name: Rewrite TransformerLab Miniforge entrypoints to a space-safe shebang path
|
- name: Ensure pinned TransformerLab environment file exists
|
||||||
|
file:
|
||||||
|
path: "{{ courseware_transformerlab_home }}/.env"
|
||||||
|
state: touch
|
||||||
|
mode: "0600"
|
||||||
|
|
||||||
|
- name: Ensure pinned TransformerLab JWT secrets exist
|
||||||
shell: |
|
shell: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
actual_prefix="{{ courseware_transformerlab_home }}/miniforge3/bin/"
|
env_file="{{ courseware_transformerlab_home }}/.env"
|
||||||
safe_prefix="{{ ansible_env.HOME }}/.transformerlab/miniforge3/bin/"
|
|
||||||
|
|
||||||
find "{{ courseware_transformerlab_home }}/miniforge3/bin" -maxdepth 1 -type f -print0 |
|
if ! grep -q '^TRANSFORMERLAB_JWT_SECRET=' "$env_file"; then
|
||||||
while IFS= read -r -d '' file; do
|
printf 'TRANSFORMERLAB_JWT_SECRET=%s\n' "$(od -An -N 64 -tx1 /dev/urandom | tr -d ' \n')" >> "$env_file"
|
||||||
first_line=$(head -n 1 "$file" || true)
|
fi
|
||||||
case "$first_line" in
|
|
||||||
"#!${actual_prefix}"*)
|
if ! grep -q '^TRANSFORMERLAB_REFRESH_SECRET=' "$env_file"; then
|
||||||
suffix=${first_line#\#!}
|
printf 'TRANSFORMERLAB_REFRESH_SECRET=%s\n' "$(od -An -N 64 -tx1 /dev/urandom | tr -d ' \n')" >> "$env_file"
|
||||||
suffix=${suffix#"${actual_prefix}"}
|
fi
|
||||||
replacement="#!${safe_prefix}${suffix}"
|
|
||||||
tmp_file=$(mktemp)
|
|
||||||
{
|
|
||||||
printf '%s\n' "$replacement"
|
|
||||||
tail -n +2 "$file"
|
|
||||||
} >"$tmp_file"
|
|
||||||
chmod --reference="$file" "$tmp_file"
|
|
||||||
mv "$tmp_file" "$file"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
args:
|
args:
|
||||||
executable: /bin/bash
|
executable: /bin/bash
|
||||||
when: "' ' in courseware_transformerlab_home"
|
|
||||||
|
|
||||||
- name: Install TransformerLab multiuser dependencies
|
- name: Remove stale TransformerLab general-uv environment
|
||||||
shell: |
|
file:
|
||||||
set -euo pipefail
|
path: "{{ courseware_transformerlab_home }}/envs/general-uv"
|
||||||
./src/install.sh multiuser_setup 2>&1 | tee "{{ courseware_logs_dir }}/transformerlab_multiuser_setup.log"
|
state: absent
|
||||||
touch "{{ courseware_transformerlab_home }}/.courseware-managed"
|
|
||||||
args:
|
|
||||||
executable: /bin/bash
|
|
||||||
chdir: "{{ courseware_transformerlab_home }}"
|
|
||||||
creates: "{{ courseware_transformerlab_home }}/envs/general-uv/bin/python"
|
|
||||||
|
|
||||||
- name: Check TransformerLab general uv environment
|
- name: Remove stale TransformerLab multiuser marker
|
||||||
stat:
|
|
||||||
path: "{{ courseware_transformerlab_home }}/envs/general-uv/bin/python"
|
|
||||||
register: courseware_transformerlab_general_uv
|
|
||||||
|
|
||||||
- name: Retry TransformerLab multiuser setup after source refresh
|
|
||||||
shell: |
|
|
||||||
set -euo pipefail
|
|
||||||
./src/install.sh multiuser_setup 2>&1 | tee "{{ courseware_logs_dir }}/transformerlab_multiuser_setup_retry.log"
|
|
||||||
args:
|
|
||||||
executable: /bin/bash
|
|
||||||
chdir: "{{ courseware_transformerlab_home }}"
|
|
||||||
when: not courseware_transformerlab_general_uv.stat.exists
|
|
||||||
|
|
||||||
- name: Recheck TransformerLab general uv environment
|
|
||||||
stat:
|
|
||||||
path: "{{ courseware_transformerlab_home }}/envs/general-uv/bin/python"
|
|
||||||
register: courseware_transformerlab_general_uv
|
|
||||||
|
|
||||||
- name: Mark TransformerLab multiuser setup complete
|
|
||||||
file:
|
file:
|
||||||
path: "{{ courseware_transformerlab_home }}/.multiuser_setup_complete"
|
path: "{{ courseware_transformerlab_home }}/.multiuser_setup_complete"
|
||||||
state: touch
|
state: absent
|
||||||
mode: "0644"
|
|
||||||
when: courseware_transformerlab_general_uv.stat.exists
|
|
||||||
|
|
||||||
- name: Fail if TransformerLab general uv environment is missing
|
- name: Determine Miniforge platform suffix
|
||||||
|
set_fact:
|
||||||
|
courseware_transformerlab_miniforge_platform: "{{ 'Linux-x86_64' if ansible_system == 'Linux' and ansible_architecture == 'x86_64' else 'Linux-aarch64' if ansible_system == 'Linux' and ansible_architecture in ['aarch64', 'arm64'] else 'MacOSX-arm64' if ansible_system == 'Darwin' and ansible_architecture == 'arm64' else 'MacOSX-x86_64' if ansible_system == 'Darwin' and ansible_architecture == 'x86_64' else 'unsupported' }}"
|
||||||
|
|
||||||
|
- name: Fail for unsupported Miniforge platform
|
||||||
fail:
|
fail:
|
||||||
msg: "TransformerLab multiuser setup completed without creating {{ courseware_transformerlab_home }}/envs/general-uv/bin/python."
|
msg: "Unsupported TransformerLab Miniforge platform: {{ ansible_system }} {{ ansible_architecture }}"
|
||||||
when: not courseware_transformerlab_general_uv.stat.exists
|
when: courseware_transformerlab_miniforge_platform == "unsupported"
|
||||||
|
|
||||||
|
- name: Download Miniforge installer for pinned TransformerLab
|
||||||
|
get_url:
|
||||||
|
url: "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-{{ courseware_transformerlab_miniforge_platform }}.sh"
|
||||||
|
dest: "{{ courseware_transformerlab_miniforge_installer }}"
|
||||||
|
mode: "0755"
|
||||||
|
when: not courseware_transformerlab_conda.stat.exists
|
||||||
|
|
||||||
|
- name: Install Miniforge for pinned TransformerLab
|
||||||
|
shell: |
|
||||||
|
set -euo pipefail
|
||||||
|
rm -rf "{{ courseware_transformerlab_home }}/miniforge3"
|
||||||
|
bash "{{ courseware_transformerlab_miniforge_installer }}" -b -p "{{ courseware_transformerlab_home }}/miniforge3"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
when: not courseware_transformerlab_conda.stat.exists
|
||||||
|
|
||||||
|
- name: Create pinned TransformerLab environment
|
||||||
|
shell: |
|
||||||
|
set -euo pipefail
|
||||||
|
"{{ courseware_transformerlab_home }}/miniforge3/bin/conda" create -y -k --prefix "{{ courseware_transformerlab_home }}/envs/transformerlab" python=3.11
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
when: not courseware_transformerlab_env_python.stat.exists
|
||||||
|
|
||||||
|
- name: Detect pinned TransformerLab accelerator profile
|
||||||
|
shell: |
|
||||||
|
set -euo pipefail
|
||||||
|
if command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi --query-gpu=name --format=csv,noheader,nounits >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' nvidia
|
||||||
|
elif command -v rocminfo >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' rocm
|
||||||
|
else
|
||||||
|
printf '%s\n' cpu
|
||||||
|
fi
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
register: courseware_transformerlab_accelerator
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Install pinned TransformerLab Python dependencies
|
||||||
|
shell: |
|
||||||
|
set -euo pipefail
|
||||||
|
env_python="{{ courseware_transformerlab_home }}/envs/transformerlab/bin/python"
|
||||||
|
conda_bin="{{ courseware_transformerlab_home }}/miniforge3/bin/conda"
|
||||||
|
accelerator="{{ courseware_transformerlab_accelerator.stdout | trim }}"
|
||||||
|
project_dir="{{ courseware_transformerlab_home }}/src"
|
||||||
|
wheel_args=()
|
||||||
|
extra="cpu"
|
||||||
|
|
||||||
|
"$env_python" -m pip install --upgrade pip
|
||||||
|
"$env_python" -m pip install --upgrade uv
|
||||||
|
|
||||||
|
if [ "$accelerator" = "nvidia" ]; then
|
||||||
|
"$conda_bin" install -y --prefix "{{ courseware_transformerlab_home }}/envs/transformerlab" cuda==12.8.1 --force-reinstall -c nvidia/label/cuda-12.8.1
|
||||||
|
extra="nvidia"
|
||||||
|
elif [ "$accelerator" = "rocm" ]; then
|
||||||
|
wheel_args+=(--index https://download.pytorch.org/whl/rocm6.4 --index-strategy unsafe-best-match)
|
||||||
|
extra="rocm"
|
||||||
|
elif [ "{{ ansible_system }}" != "Darwin" ]; then
|
||||||
|
wheel_args+=(--index https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match)
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$project_dir"
|
||||||
|
"$env_python" -m uv pip install --python "$env_python" "${wheel_args[@]}" ".[${extra}]"
|
||||||
|
|
||||||
|
if [ -d "{{ courseware_transformerlab_home }}/lab-sdk" ]; then
|
||||||
|
"$env_python" -m uv pip install --python "$env_python" "{{ courseware_transformerlab_home }}/lab-sdk"
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$env_python" -c "import uvicorn"
|
||||||
|
touch "{{ courseware_transformerlab_home }}/.install_complete"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
when: courseware_transformerlab_refresh_required or courseware_transformerlab_env_refresh_required
|
||||||
|
|
||||||
|
- name: Install TransformerLab plugin repair helper
|
||||||
|
copy:
|
||||||
|
src: "{{ playbook_dir }}/../../scripts/repair_transformerlab_plugin_supports.py"
|
||||||
|
dest: "{{ courseware_state_dir }}/repair_transformerlab_plugin_supports.py"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Install TransformerLab default-user helper
|
||||||
|
copy:
|
||||||
|
src: "{{ playbook_dir }}/../../scripts/ensure_transformerlab_user.py"
|
||||||
|
dest: "{{ courseware_state_dir }}/ensure_transformerlab_user.py"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Repair installed Fastchat plugin supports
|
||||||
|
command:
|
||||||
|
argv:
|
||||||
|
- python3
|
||||||
|
- "{{ courseware_state_dir }}/repair_transformerlab_plugin_supports.py"
|
||||||
|
- --transformerlab-dir
|
||||||
|
- "{{ courseware_transformerlab_home }}"
|
||||||
|
- --plugin
|
||||||
|
- fastchat_server
|
||||||
|
- --required-support
|
||||||
|
- chat
|
||||||
|
- --required-support
|
||||||
|
- completion
|
||||||
|
- --required-support
|
||||||
|
- visualize_model
|
||||||
|
- --required-support
|
||||||
|
- model_layers
|
||||||
|
- --required-support
|
||||||
|
- rag
|
||||||
|
- --required-support
|
||||||
|
- tools
|
||||||
|
- --required-support
|
||||||
|
- template
|
||||||
|
- --required-support
|
||||||
|
- embeddings
|
||||||
|
- --required-support
|
||||||
|
- tokenize
|
||||||
|
- --required-support
|
||||||
|
- logprobs
|
||||||
|
- --required-support
|
||||||
|
- batched
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Ensure pinned TransformerLab default user when database already exists
|
||||||
|
command:
|
||||||
|
argv:
|
||||||
|
- "{{ courseware_transformerlab_home }}/envs/transformerlab/bin/python"
|
||||||
|
- "{{ courseware_state_dir }}/ensure_transformerlab_user.py"
|
||||||
|
- --transformerlab-dir
|
||||||
|
- "{{ courseware_transformerlab_home }}"
|
||||||
|
- --email
|
||||||
|
- "{{ courseware_transformerlab_default_user_email }}"
|
||||||
|
- --password
|
||||||
|
- "{{ courseware_transformerlab_default_user_password }}"
|
||||||
|
- --first-name
|
||||||
|
- "{{ courseware_transformerlab_default_user_first_name }}"
|
||||||
|
- --last-name
|
||||||
|
- "{{ courseware_transformerlab_default_user_last_name }}"
|
||||||
|
changed_when: false
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ EMBEDDING_ATLAS_VENV="{{ courseware_venvs_dir }}/embedding-atlas"
|
|||||||
TTPS_DATASET_PATH="{{ courseware_datasets_dir }}/ttps_dataset.parquet"
|
TTPS_DATASET_PATH="{{ courseware_datasets_dir }}/ttps_dataset.parquet"
|
||||||
WIKI_TEST_RAW_PATH="{{ courseware_datasets_dir }}/wiki.test.raw"
|
WIKI_TEST_RAW_PATH="{{ courseware_datasets_dir }}/wiki.test.raw"
|
||||||
TRANSFORMERLAB_DIR="{{ courseware_transformerlab_home }}"
|
TRANSFORMERLAB_DIR="{{ courseware_transformerlab_home }}"
|
||||||
|
TRANSFORMERLAB_DEFAULT_USER_EMAIL="{{ courseware_transformerlab_default_user_email }}"
|
||||||
|
TRANSFORMERLAB_DEFAULT_USER_PASSWORD="{{ courseware_transformerlab_default_user_password }}"
|
||||||
|
TRANSFORMERLAB_DEFAULT_USER_FIRST_NAME="{{ courseware_transformerlab_default_user_first_name }}"
|
||||||
|
TRANSFORMERLAB_DEFAULT_USER_LAST_NAME="{{ courseware_transformerlab_default_user_last_name }}"
|
||||||
UNSLOTH_BIN="{{ ansible_env.HOME }}/.local/bin/unsloth"
|
UNSLOTH_BIN="{{ ansible_env.HOME }}/.local/bin/unsloth"
|
||||||
PROMPTFOO_DIR="{{ courseware_promptfoo_dir }}"
|
PROMPTFOO_DIR="{{ courseware_promptfoo_dir }}"
|
||||||
PROMPTFOO_BIN="{{ courseware_tools_dir }}/promptfoo/node_modules/.bin/promptfoo"
|
PROMPTFOO_BIN="{{ courseware_tools_dir }}/promptfoo/node_modules/.bin/promptfoo"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+8
-1
@@ -21,6 +21,10 @@ load_runtime_env() {
|
|||||||
: "${PROMPTFOO_DIR:=$COURSEWARE_STATE_DIR/lab6}"
|
: "${PROMPTFOO_DIR:=$COURSEWARE_STATE_DIR/lab6}"
|
||||||
: "${WIKI_DIR:=$COURSEWARE_STATE_DIR/repos/LLM-Labs}"
|
: "${WIKI_DIR:=$COURSEWARE_STATE_DIR/repos/LLM-Labs}"
|
||||||
: "${LLAMA_CPP_BIN_DIR:=$COURSEWARE_STATE_DIR/repos/llama.cpp/build/bin}"
|
: "${LLAMA_CPP_BIN_DIR:=$COURSEWARE_STATE_DIR/repos/llama.cpp/build/bin}"
|
||||||
|
|
||||||
|
if [ -n "${OLLAMA_BIN:-}" ] && [[ "$OLLAMA_BIN" != */* ]] && command -v "$OLLAMA_BIN" >/dev/null 2>&1; then
|
||||||
|
OLLAMA_BIN=$(command -v "$OLLAMA_BIN")
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_runtime_env() {
|
ensure_runtime_env() {
|
||||||
@@ -97,7 +101,10 @@ service_command() {
|
|||||||
"$COURSEWARE_OPEN_WEBUI_PORT"
|
"$COURSEWARE_OPEN_WEBUI_PORT"
|
||||||
;;
|
;;
|
||||||
transformerlab)
|
transformerlab)
|
||||||
printf 'cd "%s/src" && exec ./run.sh -h %s -p %s' \
|
printf 'export PATH="%s/envs/transformerlab/bin:$PATH"; export VIRTUAL_ENV="%s/envs/transformerlab"; export CONDA_PREFIX="%s/envs/transformerlab"; cd "%s/src" && exec ./run.sh -c -h %s -p %s' \
|
||||||
|
"$TRANSFORMERLAB_DIR" \
|
||||||
|
"$TRANSFORMERLAB_DIR" \
|
||||||
|
"$TRANSFORMERLAB_DIR" \
|
||||||
"$TRANSFORMERLAB_DIR" \
|
"$TRANSFORMERLAB_DIR" \
|
||||||
"$COURSEWARE_BIND_HOST" \
|
"$COURSEWARE_BIND_HOST" \
|
||||||
"$COURSEWARE_TRANSFORMERLAB_PORT"
|
"$COURSEWARE_TRANSFORMERLAB_PORT"
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import asyncio
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Ensure a known verified TransformerLab user exists for single-user courseware installs."
|
||||||
|
)
|
||||||
|
parser.add_argument("--transformerlab-dir", required=True, help="Path to the managed TransformerLab home directory.")
|
||||||
|
parser.add_argument("--email", required=True, help="Email address for the default user.")
|
||||||
|
parser.add_argument("--password", required=True, help="Password for the default user.")
|
||||||
|
parser.add_argument("--first-name", default="Student", help="Optional first name for the default user.")
|
||||||
|
parser.add_argument("--last-name", default="", help="Optional last name for the default user.")
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def bootstrap_source(transformerlab_dir: Path) -> None:
|
||||||
|
source_dir = transformerlab_dir / "src"
|
||||||
|
if not source_dir.is_dir():
|
||||||
|
raise SystemExit(f"TransformerLab source directory not found: {source_dir}")
|
||||||
|
|
||||||
|
sys.path.insert(0, str(source_dir))
|
||||||
|
|
||||||
|
env_file = transformerlab_dir / ".env"
|
||||||
|
if env_file.exists():
|
||||||
|
for line in env_file.read_text().splitlines():
|
||||||
|
if not line or line.lstrip().startswith("#") or "=" not in line:
|
||||||
|
continue
|
||||||
|
key, value = line.split("=", 1)
|
||||||
|
os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
|
||||||
|
|
||||||
|
|
||||||
|
async def ensure_user(args: argparse.Namespace) -> int:
|
||||||
|
from sqlalchemy import select
|
||||||
|
from transformerlab.db.constants import DATABASE_FILE_NAME
|
||||||
|
from transformerlab.shared.models.models import OAuthAccount, TeamRole, User, UserTeam
|
||||||
|
from transformerlab.shared.models.user_model import (
|
||||||
|
AsyncSessionLocal,
|
||||||
|
SQLAlchemyUserDatabaseWithOAuth,
|
||||||
|
create_personal_team,
|
||||||
|
)
|
||||||
|
from transformerlab.models.users import UserCreate, UserManager
|
||||||
|
|
||||||
|
database_path = Path(DATABASE_FILE_NAME)
|
||||||
|
if not database_path.exists():
|
||||||
|
print(f"TransformerLab database is not ready yet at {database_path}; skipping default-user sync.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
user_db = SQLAlchemyUserDatabaseWithOAuth(session, User, OAuthAccount)
|
||||||
|
user_manager = UserManager(user_db)
|
||||||
|
|
||||||
|
stmt = select(User).where(User.email == args.email)
|
||||||
|
result = await session.execute(stmt)
|
||||||
|
user = result.unique().scalar_one_or_none()
|
||||||
|
created = False
|
||||||
|
changed = False
|
||||||
|
|
||||||
|
if user is None:
|
||||||
|
user = await user_manager.create(
|
||||||
|
UserCreate(
|
||||||
|
email=args.email,
|
||||||
|
password=args.password,
|
||||||
|
is_active=True,
|
||||||
|
is_superuser=True,
|
||||||
|
is_verified=True,
|
||||||
|
first_name=args.first_name or None,
|
||||||
|
last_name=args.last_name or None,
|
||||||
|
),
|
||||||
|
safe=False,
|
||||||
|
request=None,
|
||||||
|
)
|
||||||
|
created = True
|
||||||
|
changed = True
|
||||||
|
else:
|
||||||
|
verified, new_hash = user_manager.password_helper.verify_and_update(args.password, user.hashed_password)
|
||||||
|
if not verified:
|
||||||
|
user.hashed_password = user_manager.password_helper.hash(args.password)
|
||||||
|
changed = True
|
||||||
|
elif new_hash:
|
||||||
|
user.hashed_password = new_hash
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
if not user.is_active:
|
||||||
|
user.is_active = True
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
if not user.is_verified:
|
||||||
|
user.is_verified = True
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
if not user.is_superuser:
|
||||||
|
user.is_superuser = True
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
if args.first_name and user.first_name != args.first_name:
|
||||||
|
user.first_name = args.first_name
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
desired_last_name = args.last_name or None
|
||||||
|
if user.last_name != desired_last_name:
|
||||||
|
user.last_name = desired_last_name
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
if changed:
|
||||||
|
session.add(user)
|
||||||
|
await session.commit()
|
||||||
|
await session.refresh(user)
|
||||||
|
|
||||||
|
team_stmt = select(UserTeam).where(UserTeam.user_id == str(user.id))
|
||||||
|
team_result = await session.execute(team_stmt)
|
||||||
|
user_team = team_result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if user_team is None:
|
||||||
|
personal_team = await create_personal_team(session, user)
|
||||||
|
user_team = UserTeam(user_id=str(user.id), team_id=personal_team.id, role=TeamRole.OWNER.value)
|
||||||
|
session.add(user_team)
|
||||||
|
await session.commit()
|
||||||
|
print(f"Created personal team '{personal_team.name}' for {args.email}.")
|
||||||
|
elif user_team.role != TeamRole.OWNER.value:
|
||||||
|
user_team.role = TeamRole.OWNER.value
|
||||||
|
session.add(user_team)
|
||||||
|
await session.commit()
|
||||||
|
print(f"Updated team role to owner for {args.email}.")
|
||||||
|
|
||||||
|
action = "Created" if created else "Verified"
|
||||||
|
print(f"{action} default TransformerLab user {args.email}.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
args = parse_args()
|
||||||
|
args.email = args.email.strip()
|
||||||
|
args.password = args.password.strip()
|
||||||
|
args.first_name = args.first_name.strip()
|
||||||
|
args.last_name = args.last_name.strip()
|
||||||
|
bootstrap_source(Path(args.transformerlab_dir))
|
||||||
|
return asyncio.run(ensure_user(args))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Repair installed TransformerLab plugin manifests from the pinned source manifest."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def load_json(path: Path) -> dict:
|
||||||
|
with path.open("r", encoding="utf-8") as handle:
|
||||||
|
return json.load(handle)
|
||||||
|
|
||||||
|
|
||||||
|
def write_json(path: Path, payload: dict) -> None:
|
||||||
|
with path.open("w", encoding="utf-8") as handle:
|
||||||
|
json.dump(payload, handle, indent=2, sort_keys=False)
|
||||||
|
handle.write("\n")
|
||||||
|
|
||||||
|
|
||||||
|
def candidate_manifest_paths(root: Path, plugin: str) -> list[Path]:
|
||||||
|
candidates = []
|
||||||
|
patterns = [
|
||||||
|
f"workspace/plugins/{plugin}/index.json",
|
||||||
|
f"orgs/*/workspace/plugins/{plugin}/index.json",
|
||||||
|
f"orgs/*/plugins/{plugin}/index.json",
|
||||||
|
]
|
||||||
|
for pattern in patterns:
|
||||||
|
candidates.extend(root.glob(pattern))
|
||||||
|
unique = []
|
||||||
|
seen = set()
|
||||||
|
for path in candidates:
|
||||||
|
resolved = path.resolve()
|
||||||
|
if resolved in seen:
|
||||||
|
continue
|
||||||
|
seen.add(resolved)
|
||||||
|
unique.append(path)
|
||||||
|
return unique
|
||||||
|
|
||||||
|
|
||||||
|
def repair_manifest(path: Path, required_supports: list[str], source_supports: list[str]) -> bool:
|
||||||
|
payload = load_json(path)
|
||||||
|
existing = payload.get("supports", [])
|
||||||
|
if not isinstance(existing, list):
|
||||||
|
existing = []
|
||||||
|
|
||||||
|
desired = []
|
||||||
|
seen = set()
|
||||||
|
for item in source_supports:
|
||||||
|
if isinstance(item, str) and item not in seen:
|
||||||
|
desired.append(item)
|
||||||
|
seen.add(item)
|
||||||
|
for item in required_supports:
|
||||||
|
if item not in seen:
|
||||||
|
desired.append(item)
|
||||||
|
seen.add(item)
|
||||||
|
|
||||||
|
if existing == desired:
|
||||||
|
return False
|
||||||
|
|
||||||
|
payload["supports"] = desired
|
||||||
|
write_json(path, payload)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--transformerlab-dir", required=True)
|
||||||
|
parser.add_argument("--plugin", required=True)
|
||||||
|
parser.add_argument("--required-support", action="append", default=[])
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
root = Path(args.transformerlab_dir).expanduser().resolve()
|
||||||
|
source_manifest = root / "src" / "transformerlab" / "plugins" / args.plugin / "index.json"
|
||||||
|
if not source_manifest.exists():
|
||||||
|
print(f"missing source plugin manifest: {source_manifest}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
source_payload = load_json(source_manifest)
|
||||||
|
source_supports = source_payload.get("supports", [])
|
||||||
|
if not isinstance(source_supports, list):
|
||||||
|
print(f"invalid supports array in {source_manifest}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
missing_required = [item for item in args.required_support if item not in source_supports]
|
||||||
|
if missing_required:
|
||||||
|
print(
|
||||||
|
f"source plugin manifest {source_manifest} is missing required supports: {', '.join(missing_required)}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
updated = 0
|
||||||
|
for manifest in candidate_manifest_paths(root, args.plugin):
|
||||||
|
if repair_manifest(manifest, args.required_support, source_supports):
|
||||||
|
updated += 1
|
||||||
|
print(f"repaired {manifest}")
|
||||||
|
|
||||||
|
if updated == 0:
|
||||||
|
print(f"no installed {args.plugin} manifests needed repair")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -9,6 +9,25 @@ load_runtime_env
|
|||||||
|
|
||||||
mkdir -p "$STATE_DIR/run" "$STATE_DIR/logs"
|
mkdir -p "$STATE_DIR/run" "$STATE_DIR/logs"
|
||||||
|
|
||||||
|
ensure_transformerlab_default_user() {
|
||||||
|
local helper_python="${TRANSFORMERLAB_DIR}/envs/transformerlab/bin/python"
|
||||||
|
|
||||||
|
if [ ! -x "$helper_python" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${TRANSFORMERLAB_DEFAULT_USER_EMAIL:-}" ] || [ -z "${TRANSFORMERLAB_DEFAULT_USER_PASSWORD:-}" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$helper_python" "$SCRIPT_DIR/ensure_transformerlab_user.py" \
|
||||||
|
--transformerlab-dir "$TRANSFORMERLAB_DIR" \
|
||||||
|
--email "$TRANSFORMERLAB_DEFAULT_USER_EMAIL" \
|
||||||
|
--password "$TRANSFORMERLAB_DEFAULT_USER_PASSWORD" \
|
||||||
|
--first-name "${TRANSFORMERLAB_DEFAULT_USER_FIRST_NAME:-Student}" \
|
||||||
|
--last-name "${TRANSFORMERLAB_DEFAULT_USER_LAST_NAME:-}" >>"$STATE_DIR/logs/transformerlab_default_user.log" 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
resolve_targets() {
|
resolve_targets() {
|
||||||
if [ $# -eq 0 ]; then
|
if [ $# -eq 0 ]; then
|
||||||
echo "No target specified." >&2
|
echo "No target specified." >&2
|
||||||
@@ -57,10 +76,13 @@ service_ready() {
|
|||||||
ollama)
|
ollama)
|
||||||
curl -fsS "$(service_url "$service")/api/tags" >/dev/null 2>&1
|
curl -fsS "$(service_url "$service")/api/tags" >/dev/null 2>&1
|
||||||
;;
|
;;
|
||||||
|
transformerlab)
|
||||||
|
curl -fsS "$(service_url "$service")/healthz" >/dev/null 2>&1
|
||||||
|
;;
|
||||||
promptfoo)
|
promptfoo)
|
||||||
curl -fsS "$(service_url "$service")/health" >/dev/null 2>&1
|
curl -fsS "$(service_url "$service")/health" >/dev/null 2>&1
|
||||||
;;
|
;;
|
||||||
open-webui|transformerlab|chunkviz|embedding-atlas|unsloth|wiki)
|
open-webui|chunkviz|embedding-atlas|unsloth|wiki)
|
||||||
curl -fsS "$(service_url "$service")" >/dev/null 2>&1
|
curl -fsS "$(service_url "$service")" >/dev/null 2>&1
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -75,13 +97,20 @@ start_one() {
|
|||||||
local log_file
|
local log_file
|
||||||
local pid_file
|
local pid_file
|
||||||
local attempt
|
local attempt
|
||||||
|
local pid_grace_attempts=5
|
||||||
|
|
||||||
if has_live_pid "$service"; then
|
if has_live_pid "$service"; then
|
||||||
|
if [ "$service" = "transformerlab" ]; then
|
||||||
|
ensure_transformerlab_default_user
|
||||||
|
fi
|
||||||
echo "$service already running"
|
echo "$service already running"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if service_ready "$service"; then
|
if service_ready "$service"; then
|
||||||
|
if [ "$service" = "transformerlab" ]; then
|
||||||
|
ensure_transformerlab_default_user
|
||||||
|
fi
|
||||||
echo "$service already available"
|
echo "$service already available"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -90,6 +119,24 @@ start_one() {
|
|||||||
open-webui)
|
open-webui)
|
||||||
start_one ollama
|
start_one ollama
|
||||||
;;
|
;;
|
||||||
|
transformerlab)
|
||||||
|
if command -v python3 >/dev/null 2>&1; then
|
||||||
|
python3 "$SCRIPT_DIR/repair_transformerlab_plugin_supports.py" \
|
||||||
|
--transformerlab-dir "$TRANSFORMERLAB_DIR" \
|
||||||
|
--plugin "fastchat_server" \
|
||||||
|
--required-support "chat" \
|
||||||
|
--required-support "completion" \
|
||||||
|
--required-support "visualize_model" \
|
||||||
|
--required-support "model_layers" \
|
||||||
|
--required-support "rag" \
|
||||||
|
--required-support "tools" \
|
||||||
|
--required-support "template" \
|
||||||
|
--required-support "embeddings" \
|
||||||
|
--required-support "tokenize" \
|
||||||
|
--required-support "logprobs" \
|
||||||
|
--required-support "batched" >>"$STATE_DIR/logs/transformerlab_plugin_supports.log" 2>&1 || true
|
||||||
|
fi
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -98,7 +145,12 @@ start_one() {
|
|||||||
log_file=$(service_log_file "$service")
|
log_file=$(service_log_file "$service")
|
||||||
pid_file=$(service_pid_file "$service")
|
pid_file=$(service_pid_file "$service")
|
||||||
|
|
||||||
if command -v setsid >/dev/null 2>&1; then
|
if [ "$service" = "ollama" ]; then
|
||||||
|
env \
|
||||||
|
OLLAMA_HOST="${COURSEWARE_BIND_HOST}:${COURSEWARE_OLLAMA_PORT}" \
|
||||||
|
OLLAMA_MODELS="$OLLAMA_MODELS_DIR" \
|
||||||
|
"$OLLAMA_BIN" serve </dev/null >>"$log_file" 2>&1 &
|
||||||
|
elif command -v setsid >/dev/null 2>&1; then
|
||||||
nohup setsid bash -lc "$cmd" </dev/null >>"$log_file" 2>&1 &
|
nohup setsid bash -lc "$cmd" </dev/null >>"$log_file" 2>&1 &
|
||||||
else
|
else
|
||||||
nohup bash -lc "$cmd" </dev/null >>"$log_file" 2>&1 &
|
nohup bash -lc "$cmd" </dev/null >>"$log_file" 2>&1 &
|
||||||
@@ -107,15 +159,20 @@ start_one() {
|
|||||||
|
|
||||||
for attempt in $(seq 1 60); do
|
for attempt in $(seq 1 60); do
|
||||||
if service_ready "$service"; then
|
if service_ready "$service"; then
|
||||||
|
if [ "$service" = "transformerlab" ]; then
|
||||||
|
ensure_transformerlab_default_user
|
||||||
|
fi
|
||||||
echo "started $service"
|
echo "started $service"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! has_live_pid "$service"; then
|
if ! has_live_pid "$service"; then
|
||||||
|
if [ "$attempt" -ge "$pid_grace_attempts" ]; then
|
||||||
rm -f "$pid_file"
|
rm -f "$pid_file"
|
||||||
echo "failed to start $service; check $log_file" >&2
|
echo "failed to start $service; check $log_file" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|||||||
Reference in New Issue
Block a user