Version 1 checkpoint

This commit is contained in:
Codex
2026-03-31 18:35:14 -06:00
parent d860318d43
commit a50172594b
14 changed files with 5007 additions and 137 deletions
+8 -1
View File
@@ -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"
+148
View File
@@ -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())
+62 -5
View File
@@ -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