@aws/ml-container-creator 0.13.5 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/config/parameter-schema-v2.json +33 -5
  2. package/infra/ci-harness/lib/ci-harness-stack.ts +13 -5
  3. package/infra/ci-harness/package-lock.json +121 -111
  4. package/infra/ci-harness/package.json +1 -1
  5. package/package.json +2 -2
  6. package/servers/endpoint-picker/index.js +23 -14
  7. package/servers/instance-sizer/index.js +72 -4
  8. package/servers/instance-sizer/lib/model-resolver.js +28 -2
  9. package/src/app.js +15 -0
  10. package/src/lib/config-loader.js +18 -0
  11. package/src/lib/config-manager.js +6 -1
  12. package/src/lib/dataset-slug.js +152 -0
  13. package/src/lib/generated/cli-options.js +9 -3
  14. package/src/lib/generated/parameter-matrix.js +15 -4
  15. package/src/lib/generated/validation-rules.js +1 -1
  16. package/src/lib/mcp-client.js +15 -1
  17. package/src/lib/mcp-query-runner.js +11 -1
  18. package/src/lib/prompt-runner.js +40 -20
  19. package/src/lib/prompts/feature-prompts.js +1 -1
  20. package/src/lib/template-manager.js +0 -7
  21. package/src/lib/template-variable-resolver.js +51 -1
  22. package/src/lib/tune-config-state.js +14 -1
  23. package/templates/do/.benchmark_writer.py +43 -0
  24. package/templates/do/.register_helper.py +1185 -0
  25. package/templates/do/.tune_helper.py +168 -2
  26. package/templates/do/__pycache__/.adapter_helper.cpython-312.pyc +0 -0
  27. package/templates/do/__pycache__/.benchmark_writer.cpython-312.pyc +0 -0
  28. package/templates/do/__pycache__/.register_helper.cpython-312.pyc +0 -0
  29. package/templates/do/__pycache__/.tune_helper.cpython-312.pyc +0 -0
  30. package/templates/do/adapter +319 -27
  31. package/templates/do/add-ic +85 -3
  32. package/templates/do/benchmark +28 -8
  33. package/templates/do/config +20 -0
  34. package/templates/do/lib/inference-component.sh +56 -3
  35. package/templates/do/register +557 -6
  36. package/templates/do/test +12 -2
  37. package/templates/do/tune +219 -6
@@ -35,6 +35,7 @@ _usage() {
35
35
  echo " add <name> --weights <s3-uri> Add a new LoRA adapter from S3"
36
36
  echo " add <name> --from-hub <hf-repo-id> Add a new LoRA adapter from HuggingFace Hub"
37
37
  echo " add <name> --from-tune [technique] Add adapter from do/tune output"
38
+ echo " add <name> --from-registry [arn] Add adapter from model registry"
38
39
  echo " list List all adapters on the endpoint"
39
40
  echo " remove <name> Remove an adapter"
40
41
  echo " update <name> --weights <new-s3-uri> Update adapter weights from S3"
@@ -53,6 +54,8 @@ _usage() {
53
54
  echo " ./do/adapter add tuned-sft --from-tune sft"
54
55
  echo " ./do/adapter add tuned-sft --from-tune --local"
55
56
  echo " ./do/adapter add tuned-sft --from-tune --no-wait"
57
+ echo " ./do/adapter add my-adapter --from-registry"
58
+ echo " ./do/adapter add my-adapter --from-registry arn:aws:sagemaker:...:model-package/proj/2"
56
59
  echo " ./do/adapter list"
57
60
  echo " ./do/adapter remove ectsum"
58
61
  echo " ./do/adapter update ectsum --weights s3://my-bucket/adapters/ectsum-v2/adapter.tar.gz"
@@ -60,7 +63,7 @@ _usage() {
60
63
  echo ""
61
64
  echo "Adapter metadata is stored in do/adapters/<name>.conf"
62
65
  echo ""
63
- echo "Note: --weights, --from-hub, and --from-tune are mutually exclusive."
66
+ echo "Note: --weights, --from-hub, --from-tune, and --from-registry are mutually exclusive."
64
67
  }
65
68
 
66
69
  # ── Validate LoRA is enabled ──────────────────────────────────────────────────
@@ -371,6 +374,8 @@ _adapter_add() {
371
374
  local from_hub=""
372
375
  local from_tune=""
373
376
  local from_tune_technique=""
377
+ local from_registry=""
378
+ local registry_arn=""
374
379
  local use_local=""
375
380
  local no_wait=""
376
381
 
@@ -406,6 +411,16 @@ _adapter_add() {
406
411
  shift
407
412
  fi
408
413
  ;;
414
+ --from-registry)
415
+ from_registry="true"
416
+ # Check if next argument is an ARN (starts with arn:)
417
+ if [ -n "${2:-}" ] && [[ "${2}" == arn:* ]]; then
418
+ registry_arn="$2"
419
+ shift 2
420
+ else
421
+ shift
422
+ fi
423
+ ;;
409
424
  --local)
410
425
  use_local="true"
411
426
  shift
@@ -418,6 +433,7 @@ _adapter_add() {
418
433
  echo "Usage: ./do/adapter add <name> --weights <s3-uri>"
419
434
  echo " ./do/adapter add <name> --from-hub <hf-repo-id>"
420
435
  echo " ./do/adapter add <name> --from-tune [technique]"
436
+ echo " ./do/adapter add <name> --from-registry [version-arn]"
421
437
  echo ""
422
438
  echo "Add a new LoRA adapter to the endpoint."
423
439
  echo ""
@@ -428,10 +444,13 @@ _adapter_add() {
428
444
  echo " --from-tune [technique] Use adapter output from do/tune"
429
445
  echo " Without technique: uses latest tune output"
430
446
  echo " With technique (e.g., sft, dpo): uses technique-specific output"
447
+ echo " --from-registry [arn] Add adapter from model registry"
448
+ echo " Without ARN: presents interactive selection"
449
+ echo " With ARN: adds directly using specified version ARN"
431
450
  echo " --local Use local aws s3 cp instead of Processing Job (--from-tune only)"
432
451
  echo " --no-wait Submit Processing Job and return immediately (--from-tune only)"
433
452
  echo ""
434
- echo "Note: --weights, --from-hub, and --from-tune are mutually exclusive."
453
+ echo "Note: --weights, --from-hub, --from-tune, and --from-registry are mutually exclusive."
435
454
  echo ""
436
455
  echo "Examples:"
437
456
  echo " ./do/adapter add ectsum --weights s3://bucket/adapters/ectsum/adapter.tar.gz"
@@ -440,6 +459,8 @@ _adapter_add() {
440
459
  echo " ./do/adapter add tuned-sft --from-tune sft"
441
460
  echo " ./do/adapter add tuned-sft --from-tune --local"
442
461
  echo " ./do/adapter add tuned-sft --from-tune --no-wait"
462
+ echo " ./do/adapter add my-adapter --from-registry"
463
+ echo " ./do/adapter add my-adapter --from-registry arn:aws:sagemaker:...:model-package/proj/2"
443
464
  exit 0
444
465
  ;;
445
466
  -*)
@@ -447,6 +468,7 @@ _adapter_add() {
447
468
  echo " Usage: ./do/adapter add <name> --weights <s3-uri>"
448
469
  echo " ./do/adapter add <name> --from-hub <hf-repo-id>"
449
470
  echo " ./do/adapter add <name> --from-tune [technique]"
471
+ echo " ./do/adapter add <name> --from-registry [version-arn]"
450
472
  exit 1
451
473
  ;;
452
474
  *)
@@ -457,6 +479,7 @@ _adapter_add() {
457
479
  echo " Usage: ./do/adapter add <name> --weights <s3-uri>"
458
480
  echo " ./do/adapter add <name> --from-hub <hf-repo-id>"
459
481
  echo " ./do/adapter add <name> --from-tune [technique]"
482
+ echo " ./do/adapter add <name> --from-registry [version-arn]"
460
483
  exit 1
461
484
  fi
462
485
  shift
@@ -478,46 +501,97 @@ _adapter_add() {
478
501
  [ -n "${weights_uri}" ] && source_count=$((source_count + 1))
479
502
  [ -n "${from_hub}" ] && source_count=$((source_count + 1))
480
503
  [ -n "${from_tune}" ] && source_count=$((source_count + 1))
504
+ [ -n "${from_registry}" ] && source_count=$((source_count + 1))
481
505
 
482
506
  if [ "${source_count}" -gt 1 ]; then
483
- echo "❌ --weights, --from-hub, and --from-tune are mutually exclusive"
507
+ echo "❌ --weights, --from-hub, --from-tune, and --from-registry are mutually exclusive"
484
508
  echo ""
485
509
  echo " Use one of:"
486
510
  echo " ./do/adapter add ${adapter_name} --weights <s3-uri>"
487
511
  echo " ./do/adapter add ${adapter_name} --from-hub <hf-repo-id>"
488
512
  echo " ./do/adapter add ${adapter_name} --from-tune [technique]"
513
+ echo " ./do/adapter add ${adapter_name} --from-registry [version-arn]"
489
514
  exit 1
490
515
  fi
491
516
 
492
517
  if [ "${source_count}" -eq 0 ]; then
493
- echo "❌ One of --weights, --from-hub, or --from-tune is required"
518
+ echo "❌ One of --weights, --from-hub, --from-tune, or --from-registry is required"
494
519
  echo " Usage: ./do/adapter add <name> --weights <s3-uri>"
495
520
  echo " ./do/adapter add <name> --from-hub <hf-repo-id>"
496
521
  echo " ./do/adapter add <name> --from-tune [technique]"
522
+ echo " ./do/adapter add <name> --from-registry [version-arn]"
497
523
  exit 1
498
524
  fi
499
525
 
500
526
  # ── Resolve --from-tune to weights_uri ────────────────────────────────
501
527
  if [ -n "${from_tune}" ]; then
502
528
  if [ -n "${from_tune_technique}" ]; then
503
- # Technique-specific: read TUNE_ADAPTER_PATH_<TECHNIQUE>
504
- local technique_upper
505
- technique_upper=$(echo "${from_tune_technique}" | tr '[:lower:]' '[:upper:]')
506
- local tune_var="TUNE_ADAPTER_PATH_${technique_upper}"
507
- local tune_path="${!tune_var:-}"
508
-
509
- if [ -z "${tune_path}" ]; then
510
- echo "❌ No adapter output found for technique: ${from_tune_technique}"
511
- echo ""
512
- echo " ${tune_var} is not set in do/config."
513
- echo ""
514
- echo " Run a tune job first:"
515
- echo " ./do/tune --technique ${from_tune_technique} --dataset <source>"
516
- exit 1
517
- fi
529
+ # Check if technique contains a hyphen — may be technique-dataset compound
530
+ if [[ "${from_tune_technique}" == *-* ]]; then
531
+ # Try compound key: TUNE_ADAPTER_PATH_<TECHNIQUE>_<SLUG>
532
+ local compound_technique="${from_tune_technique%%-*}"
533
+ local compound_slug="${from_tune_technique#*-}"
534
+ local compound_technique_upper
535
+ compound_technique_upper=$(echo "${compound_technique}" | tr '[:lower:]' '[:upper:]')
536
+ local compound_slug_upper
537
+ compound_slug_upper=$(echo "${compound_slug}" | tr '[:lower:]' '[:upper:]' | sed 's/-/_/g')
538
+ local compound_var="TUNE_ADAPTER_PATH_${compound_technique_upper}_${compound_slug_upper}"
539
+ local compound_path="${!compound_var:-}"
540
+
541
+ if [ -n "${compound_path}" ]; then
542
+ weights_uri="${compound_path}"
543
+ echo "📦 Using tune adapter output for technique '${compound_technique}' dataset '${compound_slug}': ${weights_uri}"
544
+ else
545
+ # Fallback: try as technique-only
546
+ local technique_upper
547
+ technique_upper=$(echo "${from_tune_technique}" | tr '[:lower:]' '[:upper:]' | sed 's/-/_/g')
548
+ local tune_var="TUNE_ADAPTER_PATH_${technique_upper}"
549
+ local tune_path="${!tune_var:-}"
550
+
551
+ if [ -n "${tune_path}" ]; then
552
+ echo "⚠️ ${compound_var} not found, falling back to ${tune_var}"
553
+ weights_uri="${tune_path}"
554
+ echo "📦 Using tune adapter output for technique '${from_tune_technique}': ${weights_uri}"
555
+ else
556
+ # Try the technique part alone as final fallback
557
+ local fallback_var="TUNE_ADAPTER_PATH_${compound_technique_upper}"
558
+ local fallback_path="${!fallback_var:-}"
559
+
560
+ if [ -n "${fallback_path}" ]; then
561
+ echo "⚠️ ${compound_var} not found, falling back to ${fallback_var}"
562
+ weights_uri="${fallback_path}"
563
+ echo "📦 Using tune adapter output for technique '${compound_technique}': ${weights_uri}"
564
+ else
565
+ echo "❌ No adapter output found for: ${from_tune_technique}"
566
+ echo ""
567
+ echo " Tried: ${compound_var}, ${fallback_var}"
568
+ echo ""
569
+ echo " Run a tune job first:"
570
+ echo " ./do/tune --technique ${compound_technique} --dataset <source>"
571
+ exit 1
572
+ fi
573
+ fi
574
+ fi
575
+ else
576
+ # Technique-only: read TUNE_ADAPTER_PATH_<TECHNIQUE>
577
+ local technique_upper
578
+ technique_upper=$(echo "${from_tune_technique}" | tr '[:lower:]' '[:upper:]')
579
+ local tune_var="TUNE_ADAPTER_PATH_${technique_upper}"
580
+ local tune_path="${!tune_var:-}"
581
+
582
+ if [ -z "${tune_path}" ]; then
583
+ echo "❌ No adapter output found for technique: ${from_tune_technique}"
584
+ echo ""
585
+ echo " ${tune_var} is not set in do/config."
586
+ echo ""
587
+ echo " Run a tune job first:"
588
+ echo " ./do/tune --technique ${from_tune_technique} --dataset <source>"
589
+ exit 1
590
+ fi
518
591
 
519
- weights_uri="${tune_path}"
520
- echo "📦 Using tune adapter output for technique '${from_tune_technique}': ${weights_uri}"
592
+ weights_uri="${tune_path}"
593
+ echo "📦 Using tune adapter output for technique '${from_tune_technique}': ${weights_uri}"
594
+ fi
521
595
  else
522
596
  # No technique: read TUNE_OUTPUT_PATH_LATEST and verify type
523
597
  if [ -z "${TUNE_OUTPUT_PATH_LATEST:-}" ]; then
@@ -787,6 +861,136 @@ _adapter_add() {
787
861
  fi # end --local else branch
788
862
  fi
789
863
 
864
+ # ── Resolve --from-registry to weights_uri ────────────────────────────
865
+ if [ -n "${from_registry}" ]; then
866
+ if [ -z "${registry_arn}" ]; then
867
+ # Interactive mode: query registry and present selection (AC-4.2)
868
+ # Check if stdin is a TTY (AC-4.4)
869
+ if [ ! -t 0 ]; then
870
+ echo "Error: --from-registry requires an explicit version ARN in non-interactive mode."
871
+ echo "Usage: ./do/adapter add <name> --from-registry <version-arn>"
872
+ exit 1
873
+ fi
874
+
875
+ echo "📦 Querying model registry for available adapters..."
876
+ echo ""
877
+
878
+ local adapters_json
879
+ adapters_json=$(python3 "${SCRIPT_DIR}/.register_helper.py" list-adapters \
880
+ --project-name "${PROJECT_NAME}" \
881
+ --region "${AWS_REGION}" 2>/dev/null)
882
+
883
+ # Extract JSON line (filter out any non-JSON noise)
884
+ local json_line
885
+ json_line=$(echo "${adapters_json}" | grep -E '^\{' | tail -1)
886
+
887
+ if [ -z "${json_line}" ]; then
888
+ echo "❌ Failed to query model registry."
889
+ echo " Check that your AWS credentials are configured and the project has registered adapters."
890
+ exit 1
891
+ fi
892
+
893
+ # Parse adapter count
894
+ local adapter_count
895
+ adapter_count=$(echo "${json_line}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); print(len(data.get('adapters',[])))" 2>/dev/null || echo "0")
896
+
897
+ if [ "${adapter_count}" -eq 0 ]; then
898
+ echo "❌ No adapters found in the model registry for project: ${PROJECT_NAME}"
899
+ echo ""
900
+ echo " Register an adapter first with: ./do/register (after do/tune)"
901
+ echo " Or use --weights or --from-hub to add an adapter manually."
902
+ exit 1
903
+ fi
904
+
905
+ # Display selection menu
906
+ echo "Available adapters in registry (${adapter_count} found):"
907
+ echo ""
908
+ printf ' %-4s%-10s%-12s%-20s%s\n' "#" "VERSION" "TECHNIQUE" "CREATED" "PARENT MODEL"
909
+
910
+ local i=0
911
+ while [ "${i}" -lt "${adapter_count}" ]; do
912
+ local version technique created_at parent_arn
913
+ version=$(echo "${json_line}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); print(data['adapters'][${i}].get('version','?'))" 2>/dev/null)
914
+ technique=$(echo "${json_line}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); print(data['adapters'][${i}].get('tuneTechnique','?'))" 2>/dev/null)
915
+ created_at=$(echo "${json_line}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); t=data['adapters'][${i}].get('createdAt',''); print(t[:10] if t else '?')" 2>/dev/null)
916
+ parent_arn=$(echo "${json_line}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); a=data['adapters'][${i}].get('parentModelVersionArn',''); print(a.split('/')[-2]+'/'+a.split('/')[-1] if '/' in a else a[:40])" 2>/dev/null)
917
+
918
+ local num=$((i + 1))
919
+ printf ' %-4s%-10s%-12s%-20s%s\n' "${num}" "v${version}" "${technique}" "${created_at}" "${parent_arn}"
920
+ i=$((i + 1))
921
+ done
922
+
923
+ echo ""
924
+ echo -n "Select adapter (1-${adapter_count}): "
925
+ read -r selection
926
+
927
+ # Validate selection
928
+ if ! echo "${selection}" | grep -qE '^[0-9]+$'; then
929
+ echo "❌ Invalid selection: ${selection}"
930
+ exit 1
931
+ fi
932
+ if [ "${selection}" -lt 1 ] || [ "${selection}" -gt "${adapter_count}" ]; then
933
+ echo "❌ Selection out of range. Choose 1-${adapter_count}."
934
+ exit 1
935
+ fi
936
+
937
+ # Get the ARN of the selected adapter
938
+ local sel_idx=$((selection - 1))
939
+ registry_arn=$(echo "${json_line}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); print(data['adapters'][${sel_idx}]['arn'])" 2>/dev/null)
940
+
941
+ if [ -z "${registry_arn}" ]; then
942
+ echo "❌ Failed to retrieve ARN for selected adapter."
943
+ exit 1
944
+ fi
945
+
946
+ echo ""
947
+ echo "✅ Selected: ${registry_arn}"
948
+ echo ""
949
+ fi
950
+
951
+ # Direct mode (AC-4.3): use the provided or selected ARN to get version details
952
+ echo "📦 Retrieving adapter details from registry: ${registry_arn}"
953
+ echo ""
954
+
955
+ local version_json
956
+ version_json=$(python3 "${SCRIPT_DIR}/.register_helper.py" get-version \
957
+ --arn "${registry_arn}" \
958
+ --region "${AWS_REGION}" 2>/dev/null)
959
+
960
+ local version_line
961
+ version_line=$(echo "${version_json}" | grep -E '^\{' | tail -1)
962
+
963
+ if [ -z "${version_line}" ]; then
964
+ echo "❌ Failed to get version details for: ${registry_arn}"
965
+ echo ""
966
+ echo " Check that the ARN is correct and you have sagemaker:DescribeModelPackage permission."
967
+ echo " Run ./do/adapter list to see available registry adapters."
968
+ exit 1
969
+ fi
970
+
971
+ # Check for error in response
972
+ local version_error
973
+ version_error=$(echo "${version_line}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); print(data.get('error',''))" 2>/dev/null || echo "")
974
+ if [ -n "${version_error}" ]; then
975
+ echo "❌ Registry error: ${version_error}"
976
+ exit 1
977
+ fi
978
+
979
+ # Extract model data URL (weights path)
980
+ weights_uri=$(echo "${version_line}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); print(data.get('modelDataUrl',''))" 2>/dev/null || echo "")
981
+
982
+ if [ -z "${weights_uri}" ]; then
983
+ echo "❌ No model data URL found for registry version: ${registry_arn}"
984
+ echo ""
985
+ echo " The registered adapter does not have a model data URL."
986
+ echo " Use --weights with an explicit S3 URI instead."
987
+ exit 1
988
+ fi
989
+
990
+ echo "✅ Resolved adapter weights from registry: ${weights_uri}"
991
+ echo ""
992
+ fi
993
+
790
994
  # ── Validate HF repo ID format (if --from-hub) ───────────────────────
791
995
  if [ -n "${from_hub}" ]; then
792
996
  # Valid formats: "org/name" or "name" (alphanumeric, hyphens, underscores, dots)
@@ -813,7 +1017,7 @@ _adapter_add() {
813
1017
  fi
814
1018
 
815
1019
  # ── Validate S3 URI format (only when --weights is explicitly used) ──
816
- if [ -n "${weights_uri}" ] && [ -z "${from_hub}" ] && [ -z "${from_tune}" ]; then
1020
+ if [ -n "${weights_uri}" ] && [ -z "${from_hub}" ] && [ -z "${from_tune}" ] && [ -z "${from_registry}" ]; then
817
1021
  if ! echo "${weights_uri}" | grep -qE '^s3://.*\.tar\.gz$'; then
818
1022
  echo "❌ Invalid S3 URI: ${weights_uri}"
819
1023
  echo ""
@@ -842,6 +1046,9 @@ _adapter_add() {
842
1046
  elif [ -n "${from_tune}" ]; then
843
1047
  echo " Source: do/tune output"
844
1048
  echo " Weights: ${weights_uri}"
1049
+ elif [ -n "${from_registry}" ]; then
1050
+ echo " Source: Model Registry (${registry_arn})"
1051
+ echo " Weights: ${weights_uri}"
845
1052
  else
846
1053
  echo " Weights: ${weights_uri}"
847
1054
  fi
@@ -942,9 +1149,24 @@ EOF
942
1149
 
943
1150
  # Add tune-specific metadata if --from-tune was used
944
1151
  if [ -n "${from_tune}" ]; then
1152
+ local tune_technique_meta="${from_tune_technique:-latest}"
1153
+ local tune_dataset_meta=""
1154
+ if [ -n "${from_tune_technique}" ] && [[ "${from_tune_technique}" == *-* ]]; then
1155
+ tune_technique_meta="${from_tune_technique%%-*}"
1156
+ tune_dataset_meta="${from_tune_technique#*-}"
1157
+ fi
945
1158
  cat >> "${SCRIPT_DIR}/adapters/${adapter_name}.conf" <<EOF
946
1159
  export ADAPTER_SOURCE="tune"
947
- export ADAPTER_TUNE_TECHNIQUE="${from_tune_technique:-latest}"
1160
+ export ADAPTER_TUNE_TECHNIQUE="${tune_technique_meta}"
1161
+ export ADAPTER_TUNE_DATASET="${tune_dataset_meta}"
1162
+ EOF
1163
+ fi
1164
+
1165
+ # Add registry-specific metadata if --from-registry was used
1166
+ if [ -n "${from_registry}" ]; then
1167
+ cat >> "${SCRIPT_DIR}/adapters/${adapter_name}.conf" <<EOF
1168
+ export ADAPTER_SOURCE="registry"
1169
+ export ADAPTER_REGISTRY_ARN="${registry_arn}"
948
1170
  EOF
949
1171
  fi
950
1172
 
@@ -959,6 +1181,8 @@ EOF
959
1181
  echo " Source: HuggingFace Hub (${from_hub})"
960
1182
  elif [ -n "${from_tune}" ]; then
961
1183
  echo " Source: do/tune (${from_tune_technique:-latest})"
1184
+ elif [ -n "${from_registry}" ]; then
1185
+ echo " Source: Model Registry (${registry_arn})"
962
1186
  fi
963
1187
  echo " Created: ${created_at}"
964
1188
  echo ""
@@ -1053,7 +1277,31 @@ _adapter_list() {
1053
1277
  ownership=" (external)"
1054
1278
  fi
1055
1279
 
1056
- output_lines="${output_lines}$(printf '%-14s%-12s%s%s' "${display_name}" "${status}" "${weights_url}" "${ownership}")\n"
1280
+ # Check for tuning metadata in conf file
1281
+ local tune_info=""
1282
+ if [ -d "${SCRIPT_DIR}/adapters" ]; then
1283
+ for conf_file in "${SCRIPT_DIR}"/adapters/*.conf; do
1284
+ [ -f "${conf_file}" ] || continue
1285
+ local conf_ic
1286
+ conf_ic=$(grep "^export ADAPTER_IC_NAME=" "${conf_file}" 2>/dev/null | sed 's/^export ADAPTER_IC_NAME="//' | sed 's/"$//' || echo "")
1287
+ if [ "${conf_ic}" = "${ic_name}" ]; then
1288
+ local conf_technique
1289
+ conf_technique=$(grep "^export ADAPTER_TUNE_TECHNIQUE=" "${conf_file}" 2>/dev/null | sed 's/^export ADAPTER_TUNE_TECHNIQUE="//' | sed 's/"$//' || echo "")
1290
+ local conf_dataset
1291
+ conf_dataset=$(grep "^export ADAPTER_TUNE_DATASET=" "${conf_file}" 2>/dev/null | sed 's/^export ADAPTER_TUNE_DATASET="//' | sed 's/"$//' || echo "")
1292
+ if [ -n "${conf_technique}" ]; then
1293
+ if [ -n "${conf_dataset}" ]; then
1294
+ tune_info=" (from tune: ${conf_technique} / ${conf_dataset})"
1295
+ else
1296
+ tune_info=" (from tune: ${conf_technique})"
1297
+ fi
1298
+ fi
1299
+ break
1300
+ fi
1301
+ done
1302
+ fi
1303
+
1304
+ output_lines="${output_lines}$(printf '%-14s%-12s%s%s%s' "${display_name}" "${status}" "${weights_url}" "${ownership}" "${tune_info}")\n"
1057
1305
  found_adapters=$((found_adapters + 1))
1058
1306
  done
1059
1307
 
@@ -1061,12 +1309,56 @@ _adapter_list() {
1061
1309
  echo "No adapters found on this endpoint."
1062
1310
  echo ""
1063
1311
  echo "Add one with: ./do/adapter add <name> --weights <s3-uri>"
1312
+ fi
1313
+
1314
+ if [ "${found_adapters}" -gt 0 ]; then
1315
+ # ── Print table ───────────────────────────────────────────────────────
1316
+ printf '%-14s%-12s%s\n' "NAME" "STATUS" "WEIGHTS"
1317
+ echo -e "${output_lines}" | sed '$ { /^$/d; }'
1318
+ fi
1319
+
1320
+ # ── Show registry adapters (isAdapter=true) alongside local ones ──────
1321
+ echo ""
1322
+ echo "📦 Registry adapters:"
1323
+ echo ""
1324
+
1325
+ local registry_json
1326
+ registry_json=$(python3 "${SCRIPT_DIR}/.register_helper.py" list-adapters \
1327
+ --project-name "${PROJECT_NAME}" \
1328
+ --region "${AWS_REGION}" 2>/dev/null || echo "")
1329
+
1330
+ local registry_line
1331
+ registry_line=$(echo "${registry_json}" | grep -E '^\{' | tail -1)
1332
+
1333
+ if [ -z "${registry_line}" ]; then
1334
+ echo " (could not query registry — check AWS credentials)"
1335
+ return 0
1336
+ fi
1337
+
1338
+ local registry_count
1339
+ registry_count=$(echo "${registry_line}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); print(len(data.get('adapters',[])))" 2>/dev/null || echo "0")
1340
+
1341
+ if [ "${registry_count}" -eq 0 ]; then
1342
+ echo " (none found)"
1064
1343
  return 0
1065
1344
  fi
1066
1345
 
1067
- # ── Print table ───────────────────────────────────────────────────────
1068
- printf '%-14s%-12s%s\n' "NAME" "STATUS" "WEIGHTS"
1069
- echo -e "${output_lines}" | sed '$ { /^$/d; }'
1346
+ printf ' %-10s%-12s%-14s%s\n' "VERSION" "TECHNIQUE" "CREATED" "PARENT MODEL"
1347
+
1348
+ local ri=0
1349
+ while [ "${ri}" -lt "${registry_count}" ]; do
1350
+ local rv rt rc rp
1351
+ rv=$(echo "${registry_line}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); print(data['adapters'][${ri}].get('version','?'))" 2>/dev/null)
1352
+ rt=$(echo "${registry_line}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); print(data['adapters'][${ri}].get('tuneTechnique','?'))" 2>/dev/null)
1353
+ rc=$(echo "${registry_line}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); t=data['adapters'][${ri}].get('createdAt',''); print(t[:10] if t else '?')" 2>/dev/null)
1354
+ rp=$(echo "${registry_line}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); a=data['adapters'][${ri}].get('parentModelVersionArn',''); print(a.split('/')[-2]+'/'+a.split('/')[-1] if '/' in a else a[:40])" 2>/dev/null)
1355
+
1356
+ printf ' %-10s%-12s%-14s%s\n' "v${rv}" "${rt}" "${rc}" "${rp}"
1357
+ ri=$((ri + 1))
1358
+ done
1359
+
1360
+ echo ""
1361
+ echo "Add from registry: ./do/adapter add <name> --from-registry [version-arn]"
1070
1362
  }
1071
1363
 
1072
1364
  _adapter_remove() {
@@ -123,6 +123,71 @@ if [ -n "${MODEL_DATA}" ]; then
123
123
  fi
124
124
  echo ""
125
125
 
126
+ # ============================================================
127
+ # Query model registry for available versions (AC-5.1)
128
+ # Non-intrusive: if registry query fails, skip silently
129
+ # ============================================================
130
+ REGISTRY_MODELS_JSON=""
131
+ REGISTRY_MODEL_COUNT=0
132
+ REGISTRY_SELECTED_MODEL_DATA=""
133
+ REGISTRY_SELECTED_IMAGE=""
134
+
135
+ if [ -z "${MODEL_DATA}" ]; then
136
+ # Only query registry if MODEL_DATA was not already provided via --from-tune or --model-data
137
+ if [ -t 0 ]; then
138
+ # Interactive mode: query registry for available models
139
+ _registry_json=$(python3 "${SCRIPT_DIR}/.register_helper.py" list-models \
140
+ --project-name "${PROJECT_NAME}" \
141
+ --region "${AWS_REGION:-us-east-1}" 2>/dev/null || echo "")
142
+
143
+ _registry_line=$(echo "${_registry_json}" | grep -E '^\{' | tail -1)
144
+
145
+ if [ -n "${_registry_line}" ]; then
146
+ REGISTRY_MODEL_COUNT=$(echo "${_registry_line}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); print(len(data.get('models',[])))" 2>/dev/null || echo "0")
147
+
148
+ if [ "${REGISTRY_MODEL_COUNT}" -gt 0 ]; then
149
+ REGISTRY_MODELS_JSON="${_registry_line}"
150
+
151
+ echo "📦 Available registered models:"
152
+ echo ""
153
+ printf ' %-4s%-10s%-12s%-38s%s\n' "#" "VERSION" "CONFIG" "MODEL" "INSTANCE"
154
+
155
+ _i=0
156
+ while [ "${_i}" -lt "${REGISTRY_MODEL_COUNT}" ]; do
157
+ _v=$(echo "${REGISTRY_MODELS_JSON}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); print(data['models'][${_i}].get('version','?'))" 2>/dev/null)
158
+ _c=$(echo "${REGISTRY_MODELS_JSON}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); print(data['models'][${_i}].get('deploymentConfig','?'))" 2>/dev/null)
159
+ _m=$(echo "${REGISTRY_MODELS_JSON}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); m=data['models'][${_i}].get('modelName','?'); print(m[:36]+'…' if len(m)>36 else m)" 2>/dev/null)
160
+ _inst=$(echo "${REGISTRY_MODELS_JSON}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); print(data['models'][${_i}].get('instanceType','?'))" 2>/dev/null)
161
+
162
+ _num=$((_i + 1))
163
+ printf ' %-4s%-10s%-12s%-38s%s\n' "${_num}" "v${_v}" "${_c}" "${_m}" "${_inst}"
164
+ _i=$((_i + 1))
165
+ done
166
+
167
+ echo ""
168
+ read -p "Select a model (1-${REGISTRY_MODEL_COUNT}) or press Enter to specify manually: " _selection
169
+
170
+ if [ -n "${_selection}" ]; then
171
+ # Validate selection
172
+ if echo "${_selection}" | grep -qE '^[0-9]+$' && [ "${_selection}" -ge 1 ] && [ "${_selection}" -le "${REGISTRY_MODEL_COUNT}" ]; then
173
+ _sel_idx=$((_selection - 1))
174
+ REGISTRY_SELECTED_MODEL_DATA=$(echo "${REGISTRY_MODELS_JSON}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); print(data['models'][${_sel_idx}].get('modelDataUrl',''))" 2>/dev/null || echo "")
175
+ REGISTRY_SELECTED_IMAGE=$(echo "${REGISTRY_MODELS_JSON}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); img=data['models'][${_sel_idx}].get('containerImage',''); print(img.split('/')[-1] if '/' in img else img)" 2>/dev/null || echo "")
176
+
177
+ echo ""
178
+ echo "✅ Selected registry model v$(echo "${REGISTRY_MODELS_JSON}" | python3 -c "import sys,json; data=json.loads(sys.stdin.read()); print(data['models'][${_sel_idx}].get('version','?'))" 2>/dev/null)"
179
+ echo ""
180
+ else
181
+ echo " ⚠️ Invalid selection, proceeding with manual entry."
182
+ echo ""
183
+ fi
184
+ fi
185
+ # If user pressed Enter without selection, proceed with manual entry
186
+ fi
187
+ fi
188
+ fi
189
+ fi
190
+
126
191
  # ============================================================
127
192
  # Prompt for IC name (if not provided as argument)
128
193
  # ============================================================
@@ -169,12 +234,29 @@ else
169
234
  fi
170
235
 
171
236
  # ============================================================
172
- # Prompt for image tag
237
+ # Prompt for image tag (AC-5.2, AC-5.3: pre-fill from registry, user can override)
173
238
  # ============================================================
174
- DEFAULT_IMAGE_TAG="${PROJECT_NAME}-latest"
239
+ if [ -n "${REGISTRY_SELECTED_IMAGE}" ]; then
240
+ DEFAULT_IMAGE_TAG="${REGISTRY_SELECTED_IMAGE}"
241
+ else
242
+ DEFAULT_IMAGE_TAG="${PROJECT_NAME}-latest"
243
+ fi
175
244
  read -p "Image tag [${DEFAULT_IMAGE_TAG}]: " IC_IMAGE_TAG
176
245
  IC_IMAGE_TAG="${IC_IMAGE_TAG:-${DEFAULT_IMAGE_TAG}}"
177
246
 
247
+ # ============================================================
248
+ # Prompt for model data URL (AC-5.2, AC-5.3: pre-fill from registry, user can override)
249
+ # ============================================================
250
+ if [ -z "${MODEL_DATA}" ] && [ -n "${REGISTRY_SELECTED_MODEL_DATA}" ]; then
251
+ # Pre-fill from registry selection — user can override (AC-5.3)
252
+ read -p "Model data URL [${REGISTRY_SELECTED_MODEL_DATA}]: " _model_data_input
253
+ MODEL_DATA="${_model_data_input:-${REGISTRY_SELECTED_MODEL_DATA}}"
254
+ elif [ -z "${MODEL_DATA}" ]; then
255
+ # No registry selection — offer manual entry (optional)
256
+ read -p "Model data URL (S3 URI, optional — press Enter to skip): " _model_data_input
257
+ MODEL_DATA="${_model_data_input:-}"
258
+ fi
259
+
178
260
  # ============================================================
179
261
  # Prompt for GPU count
180
262
  # ============================================================
@@ -232,7 +314,7 @@ export IC_MIN_MEMORY_MB=${IC_MIN_MEMORY_MB}
232
314
  export IC_STARTUP_TIMEOUT=900
233
315
  EOF
234
316
 
235
- # Add model data if provided (from --from-tune or --model-data)
317
+ # Add model data if provided (from --from-tune, --model-data, or registry selection)
236
318
  if [ -n "${MODEL_DATA}" ]; then
237
319
  cat >> "${IC_CONF_PATH}" <<EOF
238
320
  export IC_MODEL_DATA="${MODEL_DATA}"