qbraid-cli 0.8.5a1__py3-none-any.whl → 0.12.0a0__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.
Files changed (38) hide show
  1. qbraid_cli/_version.py +16 -14
  2. qbraid_cli/account/__init__.py +11 -0
  3. qbraid_cli/account/app.py +67 -0
  4. qbraid_cli/admin/app.py +21 -13
  5. qbraid_cli/admin/headers.py +132 -23
  6. qbraid_cli/admin/validation.py +1 -8
  7. qbraid_cli/chat/__init__.py +11 -0
  8. qbraid_cli/chat/app.py +76 -0
  9. qbraid_cli/configure/actions.py +21 -2
  10. qbraid_cli/configure/app.py +147 -2
  11. qbraid_cli/configure/claude_config.py +215 -0
  12. qbraid_cli/devices/app.py +27 -4
  13. qbraid_cli/envs/activate.py +38 -8
  14. qbraid_cli/envs/app.py +716 -89
  15. qbraid_cli/envs/create.py +3 -2
  16. qbraid_cli/files/__init__.py +11 -0
  17. qbraid_cli/files/app.py +139 -0
  18. qbraid_cli/handlers.py +35 -5
  19. qbraid_cli/jobs/app.py +33 -13
  20. qbraid_cli/jobs/toggle_braket.py +2 -13
  21. qbraid_cli/jobs/validation.py +1 -0
  22. qbraid_cli/kernels/app.py +4 -3
  23. qbraid_cli/main.py +57 -13
  24. qbraid_cli/mcp/__init__.py +10 -0
  25. qbraid_cli/mcp/app.py +126 -0
  26. qbraid_cli/mcp/serve.py +321 -0
  27. qbraid_cli/pip/app.py +2 -2
  28. qbraid_cli/pip/hooks.py +1 -0
  29. {qbraid_cli-0.8.5a1.dist-info → qbraid_cli-0.12.0a0.dist-info}/METADATA +37 -14
  30. qbraid_cli-0.12.0a0.dist-info/RECORD +46 -0
  31. {qbraid_cli-0.8.5a1.dist-info → qbraid_cli-0.12.0a0.dist-info}/WHEEL +1 -1
  32. {qbraid_cli-0.8.5a1.dist-info → qbraid_cli-0.12.0a0.dist-info/licenses}/LICENSE +6 -4
  33. qbraid_cli/admin/buildlogs.py +0 -114
  34. qbraid_cli/credits/__init__.py +0 -11
  35. qbraid_cli/credits/app.py +0 -35
  36. qbraid_cli-0.8.5a1.dist-info/RECORD +0 -39
  37. {qbraid_cli-0.8.5a1.dist-info → qbraid_cli-0.12.0a0.dist-info}/entry_points.txt +0 -0
  38. {qbraid_cli-0.8.5a1.dist-info → qbraid_cli-0.12.0a0.dist-info}/top_level.txt +0 -0
qbraid_cli/_version.py CHANGED
@@ -1,16 +1,18 @@
1
- # file generated by setuptools_scm
2
- # don't change, don't track in version control
3
- TYPE_CHECKING = False
4
- if TYPE_CHECKING:
5
- from typing import Tuple, Union
6
- VERSION_TUPLE = Tuple[Union[int, str], ...]
7
- else:
8
- VERSION_TUPLE = object
1
+ # Copyright (c) 2025, qBraid Development Team
2
+ # All rights reserved.
9
3
 
10
- version: str
11
- __version__: str
12
- __version_tuple__: VERSION_TUPLE
13
- version_tuple: VERSION_TUPLE
4
+ """
5
+ Module defining package version.
14
6
 
15
- __version__ = version = '0.8.5a1'
16
- __version_tuple__ = version_tuple = (0, 8, 5)
7
+ """
8
+
9
+ import importlib.metadata
10
+
11
+ try:
12
+ __version__ = importlib.metadata.version("qbraid-cli")
13
+ except Exception: # pylint: disable=broad-exception-caught # pragma: no cover
14
+ __version__ = "dev"
15
+
16
+ __version_tuple__ = tuple(int(part) if part.isdigit() else part for part in __version__.split("."))
17
+
18
+ __all__ = ["__version__", "__version_tuple__"]
@@ -0,0 +1,11 @@
1
+ # Copyright (c) 2024, qBraid Development Team
2
+ # All rights reserved.
3
+
4
+ """
5
+ Module defining the qbraid account namespace
6
+
7
+ """
8
+
9
+ from .app import account_app
10
+
11
+ __all__ = ["account_app"]
@@ -0,0 +1,67 @@
1
+ # Copyright (c) 2024, qBraid Development Team
2
+ # All rights reserved.
3
+
4
+ """
5
+ Module defining commands in the 'qbraid user' namespace.
6
+
7
+ """
8
+
9
+ from typing import Any
10
+
11
+ import rich
12
+ import typer
13
+
14
+ from qbraid_cli.configure.actions import QBRAID_ORG_MODEL_ENABLED
15
+ from qbraid_cli.handlers import run_progress_task
16
+
17
+ account_app = typer.Typer(help="Manage qBraid account.", no_args_is_help=True)
18
+
19
+
20
+ @account_app.command(name="credits")
21
+ def account_credits():
22
+ """Get number of qBraid credits remaining."""
23
+
24
+ def get_credits() -> float:
25
+ from qbraid_core import QbraidClient
26
+
27
+ client = QbraidClient()
28
+ return client.user_credits_value()
29
+
30
+ qbraid_credits: float = run_progress_task(get_credits)
31
+ typer.secho(
32
+ f"{typer.style('qBraid credits remaining:')} "
33
+ f"{typer.style(f'{qbraid_credits:.4f}', fg=typer.colors.MAGENTA, bold=True)}",
34
+ nl=True, # Ensure a newline after output (default is True)
35
+ )
36
+ rich.print("\nFor more information, visit: https://docs.qbraid.com/home/pricing#credits")
37
+
38
+
39
+ @account_app.command(name="info")
40
+ def account_info():
41
+ """Get qBraid account (user) metadata."""
42
+
43
+ def get_user() -> dict[str, Any]:
44
+ from qbraid_core import QbraidSession
45
+
46
+ session = QbraidSession()
47
+ user = session.get_user()
48
+ personal_info: dict = user.get("personalInformation", {})
49
+ metadata = {
50
+ "_id": user.get("_id"),
51
+ "userName": user.get("userName"),
52
+ "email": user.get("email"),
53
+ "joinedDate": user.get("createdAt", "Unknown"),
54
+ "activePlan": user.get("activePlan", "") or "Free",
55
+ "role": personal_info.get("role", "") or "guest",
56
+ }
57
+ if QBRAID_ORG_MODEL_ENABLED:
58
+ metadata["organization"] = personal_info.get("organization", "") or "qbraid"
59
+
60
+ return metadata
61
+
62
+ info = run_progress_task(get_user)
63
+ rich.print(info)
64
+
65
+
66
+ if __name__ == "__main__":
67
+ account_app()
qbraid_cli/admin/app.py CHANGED
@@ -5,16 +5,19 @@
5
5
  Module defining commands in the 'qbraid admin' namespace.
6
6
 
7
7
  """
8
+
9
+ from typing import Optional
10
+
8
11
  import typer
9
12
 
10
- from qbraid_cli.admin.buildlogs import buildlogs_app
11
- from qbraid_cli.admin.headers import check_and_fix_headers
12
- from qbraid_cli.admin.validation import validate_header_type, validate_paths_exist
13
+ from qbraid_cli.admin.headers import HeaderType, check_and_fix_headers
14
+ from qbraid_cli.admin.validation import validate_paths_exist
13
15
 
14
16
  admin_app = typer.Typer(
15
- help="CI/CD commands for qBraid maintainers.", pretty_exceptions_show_locals=False
17
+ help="CI/CD commands for qBraid maintainers.",
18
+ pretty_exceptions_show_locals=False,
19
+ no_args_is_help=True,
16
20
  )
17
- admin_app.add_typer(buildlogs_app, name="buildlogs")
18
21
 
19
22
 
20
23
  @admin_app.command(name="headers")
@@ -22,25 +25,30 @@ def admin_headers(
22
25
  src_paths: list[str] = typer.Argument(
23
26
  ..., help="Source file or directory paths to verify.", callback=validate_paths_exist
24
27
  ),
25
- header_type: str = typer.Option(
26
- "default",
27
- "--type",
28
- "-t",
29
- help="Type of header to use ('default' or 'gpl').",
30
- callback=validate_header_type,
28
+ header_type: HeaderType = typer.Option(
29
+ "default", "--type", "-t", help="Type of header to use."
31
30
  ),
32
31
  skip_files: list[str] = typer.Option(
33
- [], "--skip", "-s", help="Files to skip during verification.", callback=validate_paths_exist
32
+ [], "--skip", "-s", help="Files to skip during verification."
34
33
  ),
35
34
  fix: bool = typer.Option(
36
35
  False, "--fix", "-f", help="Whether to fix the headers instead of just verifying."
37
36
  ),
37
+ project_name: Optional[str] = typer.Option(
38
+ None, "--project", "-p", help="Name of the project to use in the header."
39
+ ),
38
40
  ):
39
41
  """
40
42
  Verifies and optionally fixes qBraid headers in specified files and directories.
41
43
 
42
44
  """
43
- check_and_fix_headers(src_paths, header_type=header_type, skip_files=skip_files, fix=fix)
45
+ check_and_fix_headers(
46
+ src_paths,
47
+ header_type=header_type,
48
+ skip_files=skip_files,
49
+ fix=fix,
50
+ project_name=project_name,
51
+ )
44
52
 
45
53
 
46
54
  if __name__ == "__main__":
@@ -1,11 +1,18 @@
1
1
  # Copyright (c) 2024, qBraid Development Team
2
2
  # All rights reserved.
3
3
 
4
+ # pylint: disable=too-many-branches,too-many-statements
5
+
4
6
  """
5
7
  Script to verify qBraid copyright file headers
6
8
 
7
9
  """
10
+
11
+ import datetime
8
12
  import os
13
+ import warnings
14
+ from enum import Enum
15
+ from pathlib import Path
9
16
  from typing import Optional
10
17
 
11
18
  import typer
@@ -13,51 +20,144 @@ from rich.console import Console
13
20
 
14
21
  from qbraid_cli.handlers import handle_error
15
22
 
16
- # pylint: disable=too-many-branches,too-many-statements
23
+ CURR_YEAR = datetime.datetime.now().year
24
+ PREV_YEAR = CURR_YEAR - 1
25
+
26
+ COMMENT_MARKER = {
27
+ ".py": "#",
28
+ ".js": "//",
29
+ ".ts": "//",
30
+ }
31
+
32
+ VALID_EXTS = tuple(COMMENT_MARKER.keys())
33
+
34
+
35
+ class HeaderType(Enum):
36
+ """Type of header to use."""
17
37
 
18
- DEFAULT_HEADER = """# Copyright (c) 2024, qBraid Development Team
38
+ default = "default" # pylint: disable=invalid-name
39
+ gpl = "gpl" # pylint: disable=invalid-name
40
+ apache = "apache" # pylint: disable=invalid-name
41
+ mit = "mit" # pylint: disable=invalid-name
42
+
43
+
44
+ DEFAULT_HEADER = f"""# Copyright (c) {CURR_YEAR}, qBraid Development Team
19
45
  # All rights reserved.
20
46
  """
21
47
 
22
- DEFAULT_HEADER_GPL = """# Copyright (C) 2024 qBraid
48
+ DEFAULT_HEADER_GPL = f"""# Copyright (C) {CURR_YEAR} qBraid
23
49
  #
24
- # This file is part of the qBraid-SDK
50
+ # This file is part of {{project_name}}
25
51
  #
26
- # The qBraid-SDK is free software released under the GNU General Public License v3
52
+ # {{project_name_start}} is free software released under the GNU General Public License v3
27
53
  # or later. You can redistribute and/or modify it under the terms of the GPL v3.
28
54
  # See the LICENSE file in the project root or <https://www.gnu.org/licenses/gpl-3.0.html>.
29
55
  #
30
- # THERE IS NO WARRANTY for the qBraid-SDK, as per Section 15 of the GPL v3.
56
+ # THERE IS NO WARRANTY for {{project_name}}, as per Section 15 of the GPL v3.
57
+ """
58
+
59
+ DEFAULT_HEADER_APACHE = f"""# Copyright {CURR_YEAR} qBraid
60
+ #
61
+ # Licensed under the Apache License, Version 2.0 (the "License");
62
+ # you may not use this file except in compliance with the License.
63
+ # You may obtain a copy of the License at
64
+ #
65
+ # http://www.apache.org/licenses/LICENSE-2.0
66
+ #
67
+ # Unless required by applicable law or agreed to in writing, software
68
+ # distributed under the License is distributed on an "AS IS" BASIS,
69
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
70
+ # See the License for the specific language governing permissions and
71
+ # limitations under the License.
72
+ """
73
+
74
+ DEFAULT_HEADER_MIT = f"""# MIT License
75
+ #
76
+ # Copyright (c) {CURR_YEAR} qBraid
77
+ #
78
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
79
+ # of this software and associated documentation files (the "Software"), to deal
80
+ # in the Software without restriction, including without limitation the rights
81
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
82
+ # copies of the Software, and to permit persons to whom the Software is
83
+ # furnished to do so, subject to the following conditions:
84
+ #
85
+ # The above copyright notice and this permission notice shall be included in all
86
+ # copies or substantial portions of the Software.
87
+ #
88
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
89
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
90
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
91
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
92
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
93
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
94
+ # SOFTWARE.
31
95
  """
32
96
 
33
97
  HEADER_TYPES = {
34
- "default": DEFAULT_HEADER,
35
- "gpl": DEFAULT_HEADER_GPL,
98
+ HeaderType.default: DEFAULT_HEADER,
99
+ HeaderType.gpl: DEFAULT_HEADER_GPL,
100
+ HeaderType.apache: DEFAULT_HEADER_APACHE,
101
+ HeaderType.mit: DEFAULT_HEADER_MIT,
36
102
  }
37
103
 
38
104
 
105
+ def get_formatted_header(header_type: HeaderType, project_name: Optional[str] = None) -> str:
106
+ """Get the formatted header based on the header type
107
+
108
+ Args:
109
+ header_type (str): The type of header to use.
110
+ project_name (str): The name of the project to use in the header.
111
+
112
+ Returns:
113
+ str: The formatted header
114
+ """
115
+
116
+ header = HEADER_TYPES[header_type]
117
+ if header_type == HeaderType.gpl:
118
+ if project_name is None:
119
+ handle_error("ValueError", "Project name is required for GPL header")
120
+
121
+ if project_name.split(" ")[0].lower() == "the":
122
+ project_name = project_name[:1].lower() + project_name[1:]
123
+ project_name_start = project_name[:1].upper() + project_name[1:]
124
+ else:
125
+ project_name_start = project_name
126
+ return header.format(project_name=project_name, project_name_start=project_name_start)
127
+
128
+ if project_name is not None:
129
+ warnings.warn(f"\nProject name is not used for header type '{header_type}'.", UserWarning)
130
+
131
+ return header
132
+
133
+
134
+ def _get_comment_marker(file_path: str, default: Optional[str] = None) -> str:
135
+ file_ext = Path(file_path).suffix
136
+ return COMMENT_MARKER.get(file_ext, default)
137
+
138
+
39
139
  def check_and_fix_headers(
40
140
  src_paths: list[str],
41
- header_type: str = "default",
141
+ header_type: HeaderType = HeaderType.default,
42
142
  skip_files: Optional[list[str]] = None,
43
143
  fix: bool = False,
144
+ project_name: Optional[str] = None,
44
145
  ) -> None:
45
146
  """Script to add or verify qBraid copyright file headers"""
46
147
  try:
47
- header = HEADER_TYPES[header_type]
148
+ header = get_formatted_header(header_type, project_name)
48
149
  except KeyError:
150
+ members = HeaderType._member_names_ # pylint: disable=no-member,protected-access
49
151
  handle_error(
50
152
  error_type="ValueError",
51
- message=(
52
- f"Invalid header type: {HEADER_TYPES}. Expected one of {list(HEADER_TYPES.keys())}"
53
- ),
153
+ message=(f"Invalid header type: {HEADER_TYPES}. Expected one of {members}"),
54
154
  )
55
155
 
56
156
  for path in src_paths:
57
157
  if not os.path.exists(path):
58
158
  handle_error(error_type="FileNotFoundError", message=f"Path '{path}' does not exist.")
59
159
 
60
- header_2023 = header.replace("2024", "2023")
160
+ header_prev_year = header.replace(str(CURR_YEAR), str(PREV_YEAR))
61
161
 
62
162
  skip_files = skip_files or []
63
163
 
@@ -73,7 +173,8 @@ def check_and_fix_headers(
73
173
  if os.path.basename(file_path) == "__init__.py":
74
174
  return not content.strip()
75
175
 
76
- skip_header_tag = "# qbraid: skip-header"
176
+ comment_marker = _get_comment_marker(file_path)
177
+ skip_header_tag = f"{comment_marker} qbraid: skip-header"
77
178
  line_number = 0
78
179
 
79
180
  for line in content.splitlines():
@@ -89,10 +190,12 @@ def check_and_fix_headers(
89
190
  with open(file_path, "r", encoding="ISO-8859-1") as f:
90
191
  content = f.read()
91
192
 
193
+ comment_marker = _get_comment_marker(file_path)
194
+
92
195
  # This finds the start of the actual content after skipping initial whitespace and comments.
93
196
  lines = content.splitlines()
94
197
  first_non_comment_line_index = next(
95
- (i for i, line in enumerate(lines) if not line.strip().startswith("#")), None
198
+ (i for i, line in enumerate(lines) if not line.strip().startswith(comment_marker)), None
96
199
  )
97
200
 
98
201
  # Prepare the content by stripping leading and trailing whitespace and separating into lines
@@ -102,10 +205,13 @@ def check_and_fix_headers(
102
205
  else ""
103
206
  )
104
207
 
208
+ updated_header = header.replace("#", comment_marker)
209
+ updated_prev_header = header_prev_year.replace("#", comment_marker)
210
+
105
211
  # Check if the content already starts with the header or if the file should be skipped
106
212
  if (
107
- content.lstrip().startswith(header)
108
- or content.lstrip().startswith(header_2023)
213
+ content.lstrip().startswith(updated_header)
214
+ or content.lstrip().startswith(updated_prev_header)
109
215
  or should_skip(file_path, content)
110
216
  ):
111
217
  return
@@ -114,7 +220,7 @@ def check_and_fix_headers(
114
220
  failed_headers.append(file_path)
115
221
  else:
116
222
  # Form the new content by combining the header, one blank line, and the actual content
117
- new_content = header.strip() + "\n\n" + actual_content
223
+ new_content = updated_header.strip() + "\n\n" + actual_content + "\n"
118
224
  with open(file_path, "w", encoding="ISO-8859-1") as f:
119
225
  f.write(new_content)
120
226
  fixed_headers.append(file_path)
@@ -125,7 +231,7 @@ def check_and_fix_headers(
125
231
  return count
126
232
  for root, _, files in os.walk(directory):
127
233
  for file in files:
128
- if file.endswith(".py"):
234
+ if file.endswith(VALID_EXTS):
129
235
  file_path = os.path.join(root, file)
130
236
  replace_or_add_header(file_path, fix)
131
237
  count += 1
@@ -135,14 +241,17 @@ def check_and_fix_headers(
135
241
  for item in src_paths:
136
242
  if os.path.isdir(item):
137
243
  checked += process_files_in_directory(item, fix)
138
- elif os.path.isfile(item) and item.endswith(".py"):
244
+ elif os.path.isfile(item) and item.endswith(VALID_EXTS):
139
245
  replace_or_add_header(item, fix)
140
246
  checked += 1
141
247
  else:
142
- handle_error(error_type="FileNotFoundError", message=f"Path '{item}' does not exist.")
248
+ if not os.path.isfile(item):
249
+ handle_error(
250
+ error_type="FileNotFoundError", message=f"Path '{item}' does not exist."
251
+ )
143
252
 
144
253
  if checked == 0:
145
- console.print("[bold]No Python files present. Nothing to do[/bold] 😴")
254
+ console.print(f"[bold]No {VALID_EXTS} files present. Nothing to do[/bold] 😴")
146
255
  raise typer.Exit(0)
147
256
 
148
257
  if not fix:
@@ -10,14 +10,7 @@ import os
10
10
 
11
11
  import typer
12
12
 
13
- from qbraid_cli.admin.headers import HEADER_TYPES
14
- from qbraid_cli.handlers import _format_list_items, validate_item
15
-
16
-
17
- def validate_header_type(value: str) -> str:
18
- """Validate header type."""
19
- header_types = list(HEADER_TYPES.keys())
20
- return validate_item(value, header_types, "Header type")
13
+ from qbraid_cli.handlers import _format_list_items
21
14
 
22
15
 
23
16
  def validate_paths_exist(paths: list[str]) -> list[str]:
@@ -0,0 +1,11 @@
1
+ # Copyright (c) 2024, qBraid Development Team
2
+ # All rights reserved.
3
+
4
+ """
5
+ Module defining the qbraid chat namespace
6
+
7
+ """
8
+
9
+ from .app import ChatFormat, list_models_callback, prompt_callback
10
+
11
+ __all__ = ["list_models_callback", "prompt_callback", "ChatFormat"]
qbraid_cli/chat/app.py ADDED
@@ -0,0 +1,76 @@
1
+ # Copyright (c) 2024, qBraid Development Team
2
+ # All rights reserved.
3
+
4
+ """
5
+ Module defining commands in the 'qbraid chat' namespace.
6
+
7
+ """
8
+ from enum import Enum
9
+
10
+ from rich.console import Console
11
+ from rich.table import Table
12
+
13
+ from qbraid_cli.handlers import handle_error, run_progress_task
14
+
15
+
16
+ class ChatFormat(str, Enum):
17
+ """Format of the response from the chat service."""
18
+
19
+ text = "text" # pylint: disable=invalid-name
20
+ code = "code" # pylint: disable=invalid-name
21
+
22
+
23
+ def list_models_callback():
24
+ """List available chat models."""
25
+ # pylint: disable-next=import-outside-toplevel
26
+ from qbraid_core.services.chat import ChatClient
27
+
28
+ client = ChatClient()
29
+
30
+ models = run_progress_task(
31
+ client.get_models,
32
+ description="Connecting to chat service...",
33
+ include_error_traceback=False,
34
+ )
35
+
36
+ console = Console()
37
+ table = Table(title="Available Chat Models\n", show_lines=True, title_justify="left")
38
+
39
+ table.add_column("Model", style="cyan", no_wrap=True)
40
+ table.add_column("Pricing [not bold](1k tokens ~750 words)", style="magenta")
41
+ table.add_column("Description", style="green")
42
+
43
+ for model in models:
44
+ table.add_row(
45
+ model["model"],
46
+ f"{model['pricing']['input']} credits / 1M input tokens\n"
47
+ f"{model['pricing']['output']} credits / 1M output tokens",
48
+ model["description"],
49
+ )
50
+
51
+ console.print(table)
52
+
53
+
54
+ def prompt_callback(prompt: str, model: str, response_format: ChatFormat, stream: bool):
55
+ """Send a chat prompt to the chat service."""
56
+ # pylint: disable-next=import-outside-toplevel
57
+ from qbraid_core.services.chat import ChatClient, ChatServiceRequestError
58
+
59
+ client = ChatClient()
60
+
61
+ if stream:
62
+ try:
63
+ for chunk in client.chat_stream(prompt, model, response_format):
64
+ print(chunk, end="")
65
+ except ChatServiceRequestError as err:
66
+ handle_error(message=str(err), include_traceback=False)
67
+ else:
68
+ content = run_progress_task(
69
+ client.chat,
70
+ prompt,
71
+ model,
72
+ response_format,
73
+ description="Connecting to chat service...",
74
+ include_error_traceback=False,
75
+ )
76
+ print(content)
@@ -15,6 +15,8 @@ import typer
15
15
  from qbraid_core.config import (
16
16
  DEFAULT_CONFIG_SECTION,
17
17
  DEFAULT_ENDPOINT_URL,
18
+ DEFAULT_ORGANIZATION,
19
+ DEFAULT_WORKSPACE,
18
20
  USER_CONFIG_PATH,
19
21
  load_config,
20
22
  save_config,
@@ -23,6 +25,8 @@ from rich.console import Console
23
25
 
24
26
  from qbraid_cli.handlers import handle_filesystem_operation
25
27
 
28
+ QBRAID_ORG_MODEL_ENABLED = False # Set to True if organization/workspace model is enabled
29
+
26
30
 
27
31
  def validate_input(key: str, value: str) -> str:
28
32
  """Validate the user input based on the key.
@@ -37,6 +41,8 @@ def validate_input(key: str, value: str) -> str:
37
41
  Raises:
38
42
  typer.BadParameter: If the value is invalid
39
43
  """
44
+ value = value.strip()
45
+
40
46
  if key == "url":
41
47
  if not re.match(r"^https?://\S+$", value):
42
48
  raise typer.BadParameter("Invalid URL format.")
@@ -44,7 +50,7 @@ def validate_input(key: str, value: str) -> str:
44
50
  if not re.match(r"^\S+@\S+\.\S+$", value):
45
51
  raise typer.BadParameter("Invalid email format.")
46
52
  elif key == "api-key":
47
- if not re.match(r"^[a-zA-Z0-9]{11}$", value):
53
+ if not re.match(r"^[a-zA-Z0-9]+$", value):
48
54
  raise typer.BadParameter("Invalid API key format.")
49
55
  return value
50
56
 
@@ -90,11 +96,24 @@ def default_action(section: str = DEFAULT_CONFIG_SECTION):
90
96
  if section not in config:
91
97
  config[section] = {}
92
98
 
93
- default_values = {"url": DEFAULT_ENDPOINT_URL}
99
+ default_values = {
100
+ "url": DEFAULT_ENDPOINT_URL,
101
+ }
102
+ if QBRAID_ORG_MODEL_ENABLED:
103
+ default_values["organization"] = DEFAULT_ORGANIZATION
104
+ default_values["workspace"] = DEFAULT_WORKSPACE
94
105
 
95
106
  config[section]["url"] = prompt_for_config(config, section, "url", default_values)
96
107
  config[section]["api-key"] = prompt_for_config(config, section, "api-key", default_values)
97
108
 
109
+ if QBRAID_ORG_MODEL_ENABLED:
110
+ config[section]["organization"] = prompt_for_config(
111
+ config, section, "organization", default_values
112
+ )
113
+ config[section]["workspace"] = prompt_for_config(
114
+ config, section, "workspace", default_values
115
+ )
116
+
98
117
  for key in list(config[section]):
99
118
  if not config[section][key]:
100
119
  del config[section][key]