amd-gaia 0.15.2__py3-none-any.whl → 0.15.3__py3-none-any.whl

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.
@@ -14,13 +14,11 @@ Main entry point for `gaia init` command that:
14
14
 
15
15
  import logging
16
16
  import os
17
+ import subprocess
17
18
  import sys
18
- import time
19
19
  from dataclasses import dataclass
20
20
  from typing import Callable, Optional
21
21
 
22
- import requests
23
-
24
22
  # Rich imports for better CLI formatting
25
23
  try:
26
24
  from rich.console import Console
@@ -44,30 +42,52 @@ INIT_PROFILES = {
44
42
  "agent": "minimal",
45
43
  "models": ["Qwen3-4B-Instruct-2507-GGUF"], # Override default minimal model
46
44
  "approx_size": "~2.5 GB",
45
+ "min_lemonade_version": "9.0.4",
46
+ "min_context_size": 4096,
47
+ },
48
+ "sd": {
49
+ "description": "Image generation with multi-modal AI (LLM + SD + VLM)",
50
+ "agent": "sd",
51
+ "models": [
52
+ "SDXL-Turbo", # Image generation (6.5GB)
53
+ "Qwen3-8B-GGUF", # Agentic reasoning + prompt enhancement (5.0GB)
54
+ "Qwen3-VL-4B-Instruct-GGUF", # Vision analysis + stories (3.2GB)
55
+ ],
56
+ "approx_size": "~15 GB",
57
+ "min_lemonade_version": "9.2.0", # SDXL-Turbo requires v9.2.0+
58
+ "min_context_size": 16384, # SD agent needs 16K for multi-step planning
47
59
  },
48
60
  "chat": {
49
61
  "description": "Interactive chat with RAG and vision support",
50
62
  "agent": "chat",
51
63
  "models": None, # Use agent profile defaults
52
64
  "approx_size": "~25 GB",
65
+ "min_lemonade_version": "9.0.4",
66
+ "min_context_size": 32768,
53
67
  },
54
68
  "code": {
55
69
  "description": "Autonomous coding assistant",
56
70
  "agent": "code",
57
71
  "models": None,
58
72
  "approx_size": "~18 GB",
73
+ "min_lemonade_version": "9.0.4",
74
+ "min_context_size": 32768,
59
75
  },
60
76
  "rag": {
61
77
  "description": "Document Q&A with retrieval",
62
78
  "agent": "rag",
63
79
  "models": None,
64
80
  "approx_size": "~25 GB",
81
+ "min_lemonade_version": "9.0.4",
82
+ "min_context_size": 32768,
65
83
  },
66
84
  "all": {
67
85
  "description": "All models for all agents",
68
86
  "agent": "all",
69
87
  "models": None,
70
88
  "approx_size": "~26 GB",
89
+ "min_lemonade_version": "9.2.0", # Includes SD, so needs v9.2.0+
90
+ "min_context_size": 32768, # Max requirement across all agents
71
91
  },
72
92
  }
73
93
 
@@ -97,6 +117,7 @@ class InitCommand:
97
117
  self,
98
118
  profile: str = "chat",
99
119
  skip_models: bool = False,
120
+ skip_lemonade: bool = False,
100
121
  force_reinstall: bool = False,
101
122
  force_models: bool = False,
102
123
  yes: bool = False,
@@ -110,6 +131,7 @@ class InitCommand:
110
131
  Args:
111
132
  profile: Profile to initialize (minimal, chat, code, rag, all)
112
133
  skip_models: Skip model downloads
134
+ skip_lemonade: Skip Lemonade installation check (for CI)
113
135
  force_reinstall: Force reinstall even if compatible version exists
114
136
  force_models: Force re-download models even if already available
115
137
  yes: Skip confirmation prompts
@@ -119,6 +141,7 @@ class InitCommand:
119
141
  """
120
142
  self.profile = profile.lower()
121
143
  self.skip_models = skip_models
144
+ self.skip_lemonade = skip_lemonade
122
145
  self.force_reinstall = force_reinstall
123
146
  self.force_models = force_models
124
147
  self.yes = yes
@@ -134,11 +157,12 @@ class InitCommand:
134
157
  # Initialize Rich console if available (before installer for console pass-through)
135
158
  self.console = Console() if RICH_AVAILABLE else None
136
159
 
137
- # Initialize AgentConsole for download progress display
160
+ # Initialize AgentConsole for formatted output
138
161
  self.agent_console = AgentConsole()
139
162
 
140
- # Use minimal installer for minimal profile
141
- use_minimal = self.profile == "minimal"
163
+ # Use minimal installer for minimal profile OR when using --yes (silent mode)
164
+ # Minimal installer is faster and more reliable for CI
165
+ use_minimal = self.profile == "minimal" or yes
142
166
 
143
167
  self.installer = LemonadeInstaller(
144
168
  target_version=LEMONADE_VERSION,
@@ -147,6 +171,10 @@ class InitCommand:
147
171
  console=self.console,
148
172
  )
149
173
 
174
+ # Context verification state (set during model loading)
175
+ self._ctx_verified = None
176
+ self._ctx_warning = None
177
+
150
178
  def _print(self, message: str, end: str = "\n"):
151
179
  """Print message to stdout."""
152
180
  if RICH_AVAILABLE and self.console:
@@ -310,12 +338,24 @@ class InitCommand:
310
338
  total_steps = 4 if not self.skip_models else 3
311
339
 
312
340
  try:
313
- # Step 1: Check/Install Lemonade (skip for remote servers)
341
+ # Step 1: Check/Install Lemonade (skip for remote servers or CI)
314
342
  if self.remote:
315
343
  self._print_step(
316
344
  1, total_steps, "Skipping local Lemonade check (remote mode)..."
317
345
  )
318
346
  self._print_success("Using remote Lemonade Server")
347
+ elif self.skip_lemonade:
348
+ self._print_step(
349
+ 1, total_steps, "Skipping Lemonade installation check..."
350
+ )
351
+ # Still show version info for transparency
352
+ info = self.installer.check_installation()
353
+ if info.installed and info.version:
354
+ self._print_success(
355
+ f"Using pre-installed Lemonade Server v{info.version}"
356
+ )
357
+ else:
358
+ self._print_success("Using pre-installed Lemonade Server")
319
359
  else:
320
360
  self._print_step(
321
361
  1, total_steps, "Checking Lemonade Server installation..."
@@ -499,10 +539,44 @@ class InitCommand:
499
539
  self._print(" This may cause compatibility issues.")
500
540
  self._print("")
501
541
 
542
+ # Check if upgrade is required based on profile's minimum version
543
+ profile_config = INIT_PROFILES[self.profile]
544
+ min_version_required = profile_config.get("min_lemonade_version", "9.0.0")
545
+ from packaging import version as pkg_version
546
+
547
+ needs_upgrade = pkg_version.parse(current_ver) < pkg_version.parse(
548
+ min_version_required
549
+ )
550
+
551
+ # In CI mode (--yes), auto-upgrade if needed for this profile
552
+ if self.yes and not self.force_reinstall:
553
+ if needs_upgrade:
554
+ self._print("")
555
+ if RICH_AVAILABLE and self.console:
556
+ self.console.print(
557
+ f" [yellow]⚠️ Profile '{self.profile}' requires Lemonade v{min_version_required}+[/yellow]"
558
+ )
559
+ self.console.print(
560
+ f" [bold cyan]Upgrading:[/bold cyan] v{current_ver} → v{target_ver}"
561
+ )
562
+ else:
563
+ self._print_warning(
564
+ f"Profile '{self.profile}' requires Lemonade v{min_version_required}+"
565
+ )
566
+ self._print(
567
+ f" Upgrading from v{current_ver} to v{target_ver}..."
568
+ )
569
+ return self._upgrade_lemonade(current_ver)
570
+ else:
571
+ self._print_success(
572
+ f"Version v{current_ver} is sufficient for profile '{self.profile}'"
573
+ )
574
+ return True
575
+
502
576
  # Prompt user to upgrade
503
577
  if not self._prompt_yes_no(
504
578
  f"Upgrade to v{target_ver}? (will uninstall current version)",
505
- default=True,
579
+ default=False, # Default to no for safety
506
580
  ):
507
581
  self._print_warning("Continuing with current version")
508
582
  return True
@@ -565,14 +639,15 @@ class InitCommand:
565
639
  self._print("")
566
640
  self._print_success("Download complete")
567
641
 
568
- # Install (not silent so desktop icon is created)
642
+ # Install (silent in CI with --yes, interactive otherwise for desktop icon)
569
643
  self.console.print(" [bold]Installing...[/bold]")
570
- self.console.print()
571
- self.console.print(
572
- " [yellow]⚠️ The installer window will appear - please complete the installation[/yellow]"
573
- )
574
- self.console.print()
575
- result = self.installer.install(installer_path, silent=False)
644
+ if not self.yes:
645
+ self.console.print()
646
+ self.console.print(
647
+ " [yellow]⚠️ The installer window will appear - please complete the installation[/yellow]"
648
+ )
649
+ self.console.print()
650
+ result = self.installer.install(installer_path, silent=self.yes)
576
651
 
577
652
  if result.success:
578
653
  self._print_success(f"Installed Lemonade v{result.version}")
@@ -722,18 +797,81 @@ class InitCommand:
722
797
  )
723
798
  return False
724
799
 
725
- # Server not running - ask user to start it manually
726
- self._print_error("Lemonade Server is not running")
727
-
728
- # In non-interactive mode (-y), fail immediately
800
+ # Server not running - start it automatically in CI mode, or prompt user
729
801
  if self.yes:
802
+ # In CI mode, just inform and auto-start (not an error)
803
+ self._print(" Lemonade Server is not running")
730
804
  self.console.print()
731
805
  self.console.print(
732
- " [dim]Start Lemonade Server and run gaia init again.[/dim]"
806
+ " [dim]Auto-starting Lemonade Server (CI mode)...[/dim]"
733
807
  )
734
- return False
735
808
 
736
- self.console.print()
809
+ try:
810
+ # Find lemonade-server executable
811
+ import shutil
812
+
813
+ # Check env var first (set by install-lemonade action in CI)
814
+ lemonade_path = os.environ.get("LEMONADE_SERVER_PATH")
815
+ if not lemonade_path:
816
+ # Fall back to PATH search
817
+ lemonade_path = shutil.which("lemonade-server")
818
+
819
+ if not lemonade_path:
820
+ raise FileNotFoundError("lemonade-server not found in PATH")
821
+
822
+ # Start server in background
823
+ if sys.platform == "win32":
824
+ # Windows: use subprocess.Popen with no window
825
+ subprocess.Popen(
826
+ [lemonade_path, "serve", "--no-tray"],
827
+ stdout=subprocess.DEVNULL,
828
+ stderr=subprocess.DEVNULL,
829
+ creationflags=(
830
+ subprocess.CREATE_NO_WINDOW
831
+ if hasattr(subprocess, "CREATE_NO_WINDOW")
832
+ else 0
833
+ ),
834
+ )
835
+ else:
836
+ # Linux/Mac: background process
837
+ subprocess.Popen(
838
+ [lemonade_path, "serve"],
839
+ stdout=subprocess.DEVNULL,
840
+ stderr=subprocess.DEVNULL,
841
+ )
842
+
843
+ # Wait for server to start
844
+ import time
845
+
846
+ max_wait = 30
847
+ waited = 0
848
+ while waited < max_wait:
849
+ time.sleep(2)
850
+ waited += 2
851
+ try:
852
+ health = client.health_check()
853
+ if (
854
+ health
855
+ and isinstance(health, dict)
856
+ and health.get("status") == "ok"
857
+ ):
858
+ self._print_success(
859
+ f"Server started and ready (waited {waited}s)"
860
+ )
861
+ return True
862
+ except Exception:
863
+ pass
864
+
865
+ self._print_error(f"Server failed to start after {max_wait}s")
866
+ return False
867
+
868
+ except Exception as e:
869
+ self._print_error(f"Failed to start server: {e}")
870
+ return False
871
+ else:
872
+ # Interactive mode - prompt user to start manually
873
+ self._print_error("Lemonade Server is not running")
874
+ self.console.print()
737
875
  self.console.print(" [bold]Please start Lemonade Server:[/bold]")
738
876
  if sys.platform == "win32":
739
877
  self.console.print(
@@ -841,11 +979,13 @@ class InitCommand:
841
979
  # Use agent profile defaults
842
980
  model_ids = client.get_required_models(agent)
843
981
 
844
- # Always include the default CPU model (used by gaia llm)
845
- from gaia.llm.lemonade_client import DEFAULT_MODEL_NAME
982
+ # Include default CPU model for profiles that need gaia llm
983
+ # SD profile has its own LLM (Qwen3-8B) and doesn't need the 0.5B model
984
+ if self.profile != "sd":
985
+ from gaia.llm.lemonade_client import DEFAULT_MODEL_NAME
846
986
 
847
- if DEFAULT_MODEL_NAME not in model_ids:
848
- model_ids = list(model_ids) + [DEFAULT_MODEL_NAME]
987
+ if DEFAULT_MODEL_NAME not in model_ids:
988
+ model_ids = list(model_ids) + [DEFAULT_MODEL_NAME]
849
989
 
850
990
  if not model_ids:
851
991
  self._print_success("No models required for this profile")
@@ -886,63 +1026,96 @@ class InitCommand:
886
1026
  except Exception as e:
887
1027
  self._print_error(f"Failed to delete {model_id}: {e}")
888
1028
 
889
- # Download each model
1029
+ # Find lemonade-server executable
1030
+ lemonade_path = self._find_lemonade_server()
1031
+ if not lemonade_path:
1032
+ self._print_error("Could not find lemonade-server executable")
1033
+ self._print(
1034
+ " Please ensure Lemonade Server is installed and in your PATH"
1035
+ )
1036
+ return False
1037
+
1038
+ # Download each model using CLI
890
1039
  success = True
891
1040
  for model_id in model_ids:
892
1041
  self._print("")
893
-
894
- # Use AgentConsole for nicely formatted download progress
895
- self.agent_console.print_download_start(model_id)
1042
+ self.agent_console.print(
1043
+ f" [bold cyan]Downloading:[/bold cyan] {model_id}"
1044
+ )
896
1045
 
897
1046
  try:
898
- event_count = 0
899
- last_bytes = 0
900
- last_time = time.time()
901
-
902
- for event in client.pull_model_stream(model_name=model_id):
903
- event_count += 1
904
- event_type = event.get("event")
905
-
906
- if event_type == "progress":
907
- # Skip first 2 spurious events from Lemonade
908
- if event_count <= 2:
909
- continue
910
-
911
- # Calculate download speed
912
- current_bytes = event.get("bytes_downloaded", 0)
913
- current_time = time.time()
914
- time_delta = current_time - last_time
915
-
916
- speed_mbps = 0.0
917
- if time_delta > 0.1 and current_bytes > last_bytes:
918
- bytes_delta = current_bytes - last_bytes
919
- speed_mbps = (bytes_delta / time_delta) / (1024 * 1024)
920
- last_bytes = current_bytes
921
- last_time = current_time
922
-
923
- self.agent_console.print_download_progress(
924
- percent=event.get("percent", 0),
925
- bytes_downloaded=current_bytes,
926
- bytes_total=event.get("bytes_total", 0),
927
- speed_mbps=speed_mbps,
928
- )
1047
+ # Use lemonade-server CLI pull command with visible progress
1048
+ result = subprocess.run(
1049
+ [lemonade_path, "pull", model_id],
1050
+ check=False,
1051
+ )
929
1052
 
930
- elif event_type == "complete":
931
- self.agent_console.print_download_complete(model_id)
1053
+ self._print("")
1054
+ if result.returncode == 0:
1055
+ # Verify the model was actually downloaded successfully
1056
+ # Check if model is now available (not just exit code)
1057
+ if client.check_model_available(model_id):
1058
+ self._print_success(f"Downloaded {model_id}")
1059
+ else:
1060
+ # Pull succeeded but model not available - likely validation error
1061
+ self._print_error(
1062
+ f"Download validation failed for {model_id}"
1063
+ )
1064
+ self.agent_console.print(
1065
+ " [yellow]Corrupted download detected. Deleting and retrying...[/yellow]"
1066
+ )
932
1067
 
933
- elif event_type == "error":
934
- self.agent_console.print_download_error(
935
- event.get("error", "Unknown error"), model_id
1068
+ # Delete the corrupted model
1069
+ delete_result = subprocess.run(
1070
+ [lemonade_path, "delete", model_id],
1071
+ check=False,
1072
+ capture_output=True,
936
1073
  )
937
- success = False
938
- break
939
1074
 
940
- except requests.exceptions.ConnectionError as e:
941
- self.agent_console.print_download_error(f"Connection error: {e}")
942
- self._print(" Check your network connection and retry")
1075
+ if delete_result.returncode == 0:
1076
+ self.agent_console.print(
1077
+ f" [dim]Deleted corrupted {model_id}[/dim]"
1078
+ )
1079
+
1080
+ # Retry download
1081
+ self.agent_console.print(
1082
+ f" [bold cyan]Retrying download:[/bold cyan] {model_id}"
1083
+ )
1084
+ retry_result = subprocess.run(
1085
+ [lemonade_path, "pull", model_id],
1086
+ check=False,
1087
+ )
1088
+
1089
+ self._print("")
1090
+ if (
1091
+ retry_result.returncode == 0
1092
+ and client.check_model_available(model_id)
1093
+ ):
1094
+ self._print_success(
1095
+ f"Downloaded {model_id} (retry successful)"
1096
+ )
1097
+ else:
1098
+ self._print_error(f"Retry failed for {model_id}")
1099
+ success = False
1100
+ else:
1101
+ self._print_error(
1102
+ f"Failed to delete corrupted model {model_id}"
1103
+ )
1104
+ success = False
1105
+ else:
1106
+ self._print_error(
1107
+ f"Failed to download {model_id} (exit code: {result.returncode})"
1108
+ )
1109
+ success = False
1110
+
1111
+ except FileNotFoundError:
1112
+ self._print("")
1113
+ self._print_error(f"lemonade-server not found at: {lemonade_path}")
943
1114
  success = False
1115
+ break
944
1116
  except Exception as e:
945
- self.agent_console.print_download_error(str(e), model_id)
1117
+ self._print("")
1118
+ self._print_error(f"Error downloading {model_id}: {e}")
946
1119
  success = False
947
1120
 
948
1121
  return success
@@ -963,13 +1136,92 @@ class InitCommand:
963
1136
  Tuple of (success: bool, error_message: str or None)
964
1137
  """
965
1138
  try:
966
- # Load the model first
967
- client.load_model(model_id, auto_download=False, prompt=False)
1139
+ # Check if profile requires specific context size for this model
1140
+ profile_config = INIT_PROFILES.get(self.profile, {})
1141
+ min_ctx = profile_config.get("min_context_size")
1142
+
1143
+ # Load the model (with context size if required)
1144
+ is_llm = not (
1145
+ "embed" in model_id.lower()
1146
+ or any(sd in model_id.upper() for sd in ["SDXL", "SD-", "SD1", "SD2"])
1147
+ )
1148
+
1149
+ if is_llm and min_ctx:
1150
+ # Force unload if already loaded to ensure recipe_options are saved
1151
+ if client.check_model_loaded(model_id):
1152
+ client.unload_model()
1153
+
1154
+ # Load with explicit context size and save it
1155
+ client.load_model(
1156
+ model_id,
1157
+ auto_download=False,
1158
+ prompt=False,
1159
+ ctx_size=min_ctx,
1160
+ save_options=True,
1161
+ )
1162
+
1163
+ # Verify context size was set correctly by reading it back
1164
+ try:
1165
+ # Get full model list with recipe_options
1166
+ models_list = client.list_models()
1167
+ model_info = next(
1168
+ (
1169
+ m
1170
+ for m in models_list.get("data", [])
1171
+ if m.get("id") == model_id
1172
+ ),
1173
+ None,
1174
+ )
1175
+
1176
+ if not model_info:
1177
+ return (False, "Model info not found")
1178
+
1179
+ actual_ctx = model_info.get("recipe_options", {}).get("ctx_size")
968
1180
 
969
- # Check if this is an embedding model
1181
+ if actual_ctx and actual_ctx >= min_ctx:
1182
+ # Success - context verified
1183
+ # Store for success message, and flag if larger than expected
1184
+ self._ctx_verified = actual_ctx
1185
+ if actual_ctx > min_ctx:
1186
+ self._ctx_warning = (
1187
+ f"(configured: {actual_ctx}, required: {min_ctx})"
1188
+ )
1189
+ elif actual_ctx:
1190
+ # Context was set but is too small
1191
+ return (False, f"Context {actual_ctx} < {min_ctx} required")
1192
+ else:
1193
+ # Context not in recipe_options - should not happen after forced unload/reload
1194
+ # Mark as unverified but don't fail the test
1195
+ self._ctx_verified = None # Explicitly mark as unverified
1196
+ except Exception as e:
1197
+ return (False, f"Context check failed: {str(e)[:50]}")
1198
+ else:
1199
+ # Load without context size (SD models, embedding models, or no requirement)
1200
+ client.load_model(model_id, auto_download=False, prompt=False)
1201
+
1202
+ # Check model type
970
1203
  is_embedding_model = "embed" in model_id.lower()
1204
+ is_sd_model = any(
1205
+ sd in model_id.upper() for sd in ["SDXL", "SD-", "SD1", "SD2"]
1206
+ )
971
1207
 
972
- if is_embedding_model:
1208
+ if is_sd_model:
1209
+ # Test SD model with image generation
1210
+ response = client.generate_image(
1211
+ prompt="test",
1212
+ model=model_id,
1213
+ steps=1, # Minimal steps for quick test
1214
+ size="512x512",
1215
+ )
1216
+ # Check if we got a valid image in b64_json format
1217
+ if (
1218
+ response
1219
+ and response.get("data")
1220
+ and response["data"][0].get("b64_json")
1221
+ ):
1222
+ return (True, None)
1223
+ return (False, "No image generated")
1224
+ elif is_embedding_model:
973
1225
  # Test embedding model with a simple text
974
1226
  response = client.embeddings(
975
1227
  input_texts=["test"],
@@ -1031,6 +1283,28 @@ class InitCommand:
1031
1283
  self._print_error("Server not responding")
1032
1284
  return False
1033
1285
 
1286
+ # Ensure proper context size for this profile
1287
+ profile_config = INIT_PROFILES[self.profile]
1288
+ min_ctx = profile_config.get("min_context_size")
1289
+ if min_ctx:
1290
+ from gaia.llm.lemonade_manager import LemonadeManager
1291
+
1292
+ self.console.print()
1293
+ self.console.print(
1294
+ f" [dim]Ensuring {min_ctx} token context for {self.profile} profile...[/dim]"
1295
+ )
1296
+ success = LemonadeManager.ensure_ready(
1297
+ min_context_size=min_ctx, quiet=True
1298
+ )
1299
+ if success:
1300
+ self._print_success(f"Context size verified: {min_ctx} tokens")
1301
+ else:
1302
+ self._print_error(f"Failed to configure {min_ctx} token context")
1303
+ self._print_error(
1304
+ f"Try: lemonade-server serve --ctx-size {min_ctx}"
1305
+ )
1306
+ return False
1307
+
1034
1308
  # Get models to verify
1035
1309
  profile_config = INIT_PROFILES[self.profile]
1036
1310
  if profile_config["models"]:
@@ -1038,11 +1312,13 @@ class InitCommand:
1038
1312
  else:
1039
1313
  model_ids = client.get_required_models(profile_config["agent"])
1040
1314
 
1041
- # Always include the default CPU model (used by gaia llm)
1042
- from gaia.llm.lemonade_client import DEFAULT_MODEL_NAME
1315
+ # Include default CPU model for profiles that need gaia llm
1316
+ # SD profile has its own LLM and doesn't need the 0.5B model
1317
+ if self.profile != "sd":
1318
+ from gaia.llm.lemonade_client import DEFAULT_MODEL_NAME
1043
1319
 
1044
- if DEFAULT_MODEL_NAME not in model_ids:
1045
- model_ids = list(model_ids) + [DEFAULT_MODEL_NAME]
1320
+ if DEFAULT_MODEL_NAME not in model_ids:
1321
+ model_ids = list(model_ids) + [DEFAULT_MODEL_NAME]
1046
1322
 
1047
1323
  if not model_ids or self.skip_models:
1048
1324
  return True
@@ -1064,7 +1340,6 @@ class InitCommand:
1064
1340
  # Test each model with a small inference request
1065
1341
  self.console.print()
1066
1342
  self.console.print(" [bold]Testing models with inference:[/bold]")
1067
- self.console.print(" [yellow]⚠️ Press Ctrl+C to skip.[/yellow]")
1068
1343
 
1069
1344
  models_passed = 0
1070
1345
  models_failed = []
@@ -1091,8 +1366,25 @@ class InitCommand:
1091
1366
  # Clear the line and show result
1092
1367
  print("\r" + " " * 80 + "\r", end="")
1093
1368
  if success:
1369
+ # Check if context was verified
1370
+ ctx_msg = ""
1371
+ if hasattr(self, "_ctx_verified"):
1372
+ if self._ctx_verified:
1373
+ # Context successfully verified
1374
+ ctx_msg = f" [dim](ctx: {self._ctx_verified})[/dim]"
1375
+
1376
+ # Warn if context is larger than required
1377
+ if hasattr(self, "_ctx_warning"):
1378
+ ctx_msg = f" [yellow]{self._ctx_warning}[/yellow]"
1379
+ delattr(self, "_ctx_warning")
1380
+ elif self._ctx_verified is None:
1381
+ # Context could not be verified
1382
+ ctx_msg = " [yellow]⚠️ Context unverified![/yellow]"
1383
+
1384
+ delattr(self, "_ctx_verified") # Reset for next model
1385
+
1094
1386
  self.console.print(
1095
- f" [green]✓[/green] [cyan]{model_id}[/cyan] [dim]- OK[/dim]"
1387
+ f" [green]✓[/green] [cyan]{model_id}[/cyan] [dim]- OK[/dim]{ctx_msg}"
1096
1388
  )
1097
1389
  models_passed += 1
1098
1390
  else:
@@ -1189,24 +1481,43 @@ class InitCommand:
1189
1481
  )
1190
1482
  self.console.print()
1191
1483
  self.console.print(" [bold]Quick start commands:[/bold]")
1192
- self.console.print(
1193
- " [cyan]gaia chat[/cyan] Start interactive chat"
1194
- )
1195
- self.console.print(
1196
- " [cyan]gaia llm 'Hello'[/cyan] Quick LLM query"
1197
- )
1198
- self.console.print(
1199
- " [cyan]gaia talk[/cyan] Voice interaction"
1200
- )
1201
- self.console.print()
1202
1484
 
1203
- profile_config = INIT_PROFILES[self.profile]
1204
- if profile_config["agent"] == "minimal":
1485
+ # Profile-specific quick start commands
1486
+ if self.profile == "sd":
1205
1487
  self.console.print(
1206
- " [dim]Note: Minimal profile installed. For full features, run:[/dim]"
1488
+ ' [cyan]gaia sd "create a cute robot kitten and tell me a story"[/cyan]'
1489
+ )
1490
+ self.console.print(' [cyan]gaia sd "sunset over mountains"[/cyan]')
1491
+ self.console.print(
1492
+ " [cyan]gaia sd -i[/cyan] Interactive mode"
1493
+ )
1494
+ elif self.profile == "chat":
1495
+ self.console.print(
1496
+ " [cyan]gaia chat[/cyan] Start interactive chat with RAG"
1497
+ )
1498
+ self.console.print(
1499
+ " [cyan]gaia chat init[/cyan] Setup document folder"
1500
+ )
1501
+ elif self.profile == "minimal":
1502
+ self.console.print(
1503
+ " [cyan]gaia llm 'Hello'[/cyan] Quick LLM query"
1504
+ )
1505
+ self.console.print(
1506
+ " [dim]Note: Minimal profile installed. For full features, run:[/dim]"
1207
1507
  )
1208
1508
  self.console.print(" [cyan]gaia init --profile chat[/cyan]")
1209
- self.console.print()
1509
+ else:
1510
+ # Default commands for other profiles
1511
+ self.console.print(
1512
+ " [cyan]gaia chat[/cyan] Start interactive chat"
1513
+ )
1514
+ self.console.print(
1515
+ " [cyan]gaia llm 'Hello'[/cyan] Quick LLM query"
1516
+ )
1517
+ self.console.print(
1518
+ " [cyan]gaia talk[/cyan] Voice interaction"
1519
+ )
1520
+ self.console.print()
1210
1521
  else:
1211
1522
  self._print("")
1212
1523
  self._print("=" * 60)
@@ -1214,23 +1525,40 @@ class InitCommand:
1214
1525
  self._print("=" * 60)
1215
1526
  self._print("")
1216
1527
  self._print(" Quick start commands:")
1217
- self._print(" gaia chat # Start interactive chat")
1218
- self._print(" gaia llm 'Hello' # Quick LLM query")
1219
- self._print(" gaia talk # Voice interaction")
1220
- self._print("")
1221
1528
 
1222
- profile_config = INIT_PROFILES[self.profile]
1223
- if profile_config["agent"] == "minimal":
1529
+ # Profile-specific quick start commands
1530
+ if self.profile == "sd":
1531
+ self._print(
1532
+ ' gaia sd "create a cute robot kitten and tell me a story"'
1533
+ )
1534
+ self._print(' gaia sd "sunset over mountains"')
1535
+ self._print(
1536
+ " gaia sd -i # Interactive mode"
1537
+ )
1538
+ elif self.profile == "chat":
1539
+ self._print(
1540
+ " gaia chat # Start interactive chat with RAG"
1541
+ )
1542
+ self._print(" gaia chat init # Setup document folder")
1543
+ elif self.profile == "minimal":
1544
+ self._print(" gaia llm 'Hello' # Quick LLM query")
1545
+ self._print("")
1224
1546
  self._print(
1225
1547
  " Note: Minimal profile installed. For full features, run:"
1226
1548
  )
1227
1549
  self._print(" gaia init --profile chat")
1228
- self._print("")
1550
+ else:
1551
+ # Default commands for other profiles
1552
+ self._print(" gaia chat # Start interactive chat")
1553
+ self._print(" gaia llm 'Hello' # Quick LLM query")
1554
+ self._print(" gaia talk # Voice interaction")
1555
+ self._print("")
1229
1556
 
1230
1557
 
1231
1558
  def run_init(
1232
1559
  profile: str = "chat",
1233
1560
  skip_models: bool = False,
1561
+ skip_lemonade: bool = False,
1234
1562
  force_reinstall: bool = False,
1235
1563
  force_models: bool = False,
1236
1564
  yes: bool = False,
@@ -1243,6 +1571,7 @@ def run_init(
1243
1571
  Args:
1244
1572
  profile: Profile to initialize (minimal, chat, code, rag, all)
1245
1573
  skip_models: Skip model downloads
1574
+ skip_lemonade: Skip Lemonade installation check (for CI)
1246
1575
  force_reinstall: Force reinstall even if compatible version exists
1247
1576
  force_models: Force re-download models (deletes then re-downloads)
1248
1577
  yes: Skip confirmation prompts
@@ -1256,6 +1585,7 @@ def run_init(
1256
1585
  cmd = InitCommand(
1257
1586
  profile=profile,
1258
1587
  skip_models=skip_models,
1588
+ skip_lemonade=skip_lemonade,
1259
1589
  force_reinstall=force_reinstall,
1260
1590
  force_models=force_models,
1261
1591
  yes=yes,