Version 1 checkpoint
This commit is contained in:
+8
-1
@@ -21,6 +21,10 @@ load_runtime_env() {
|
||||
: "${PROMPTFOO_DIR:=$COURSEWARE_STATE_DIR/lab6}"
|
||||
: "${WIKI_DIR:=$COURSEWARE_STATE_DIR/repos/LLM-Labs}"
|
||||
: "${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() {
|
||||
@@ -97,7 +101,10 @@ service_command() {
|
||||
"$COURSEWARE_OPEN_WEBUI_PORT"
|
||||
;;
|
||||
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" \
|
||||
"$COURSEWARE_BIND_HOST" \
|
||||
"$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"
|
||||
|
||||
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() {
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "No target specified." >&2
|
||||
@@ -57,10 +76,13 @@ service_ready() {
|
||||
ollama)
|
||||
curl -fsS "$(service_url "$service")/api/tags" >/dev/null 2>&1
|
||||
;;
|
||||
transformerlab)
|
||||
curl -fsS "$(service_url "$service")/healthz" >/dev/null 2>&1
|
||||
;;
|
||||
promptfoo)
|
||||
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
|
||||
;;
|
||||
*)
|
||||
@@ -75,13 +97,20 @@ start_one() {
|
||||
local log_file
|
||||
local pid_file
|
||||
local attempt
|
||||
local pid_grace_attempts=5
|
||||
|
||||
if has_live_pid "$service"; then
|
||||
if [ "$service" = "transformerlab" ]; then
|
||||
ensure_transformerlab_default_user
|
||||
fi
|
||||
echo "$service already running"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if service_ready "$service"; then
|
||||
if [ "$service" = "transformerlab" ]; then
|
||||
ensure_transformerlab_default_user
|
||||
fi
|
||||
echo "$service already available"
|
||||
return 0
|
||||
fi
|
||||
@@ -90,6 +119,24 @@ start_one() {
|
||||
open-webui)
|
||||
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
|
||||
@@ -98,7 +145,12 @@ start_one() {
|
||||
log_file=$(service_log_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 &
|
||||
else
|
||||
nohup bash -lc "$cmd" </dev/null >>"$log_file" 2>&1 &
|
||||
@@ -107,14 +159,19 @@ start_one() {
|
||||
|
||||
for attempt in $(seq 1 60); do
|
||||
if service_ready "$service"; then
|
||||
if [ "$service" = "transformerlab" ]; then
|
||||
ensure_transformerlab_default_user
|
||||
fi
|
||||
echo "started $service"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! has_live_pid "$service"; then
|
||||
rm -f "$pid_file"
|
||||
echo "failed to start $service; check $log_file" >&2
|
||||
exit 1
|
||||
if [ "$attempt" -ge "$pid_grace_attempts" ]; then
|
||||
rm -f "$pid_file"
|
||||
echo "failed to start $service; check $log_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
|
||||
Reference in New Issue
Block a user