-
Notifications
You must be signed in to change notification settings - Fork 7
Adding Luma API docs #612
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding Luma API docs #612
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,11 +6,13 @@ | |||||||||||||||||||||||||||||||||||||||||
| import sys | ||||||||||||||||||||||||||||||||||||||||||
| from functools import partial | ||||||||||||||||||||||||||||||||||||||||||
| from pathlib import Path | ||||||||||||||||||||||||||||||||||||||||||
| from typing import Optional, Tuple | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| import openai | ||||||||||||||||||||||||||||||||||||||||||
| from dotenv import load_dotenv | ||||||||||||||||||||||||||||||||||||||||||
| from InquirerPy import inquirer | ||||||||||||||||||||||||||||||||||||||||||
| from rich.console import Console | ||||||||||||||||||||||||||||||||||||||||||
| import typer | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| from discovery import find_toolkits_directories | ||||||||||||||||||||||||||||||||||||||||||
| from docs_builder import ( | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -55,18 +57,33 @@ def save_toolkits_dir(toolkits_dir: str) -> None: | |||||||||||||||||||||||||||||||||||||||||
| f.write(f"{key}={value}\n") | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| def get_toolkits_dir() -> str: | ||||||||||||||||||||||||||||||||||||||||||
| def get_toolkits_dir(provided_dir: Optional[str] = None, interactive: bool = True) -> str: | ||||||||||||||||||||||||||||||||||||||||||
| """Get or prompt for the toolkits directory path. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| First tries to use saved directory, then auto-discovers, then prompts manually. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||||||||||||||||||
| provided_dir: If provided, use this directory directly. | ||||||||||||||||||||||||||||||||||||||||||
| interactive: Whether to use interactive prompts. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Returns: | ||||||||||||||||||||||||||||||||||||||||||
| Path to the toolkits directory. | ||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||
| # If directory is provided via CLI, use it directly | ||||||||||||||||||||||||||||||||||||||||||
| if provided_dir: | ||||||||||||||||||||||||||||||||||||||||||
| if os.path.isdir(provided_dir): | ||||||||||||||||||||||||||||||||||||||||||
| return provided_dir | ||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||
| console.print(f"❌ Provided directory does not exist: {provided_dir}", style="bold red") | ||||||||||||||||||||||||||||||||||||||||||
| sys.exit(1) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| toolkits_dir = os.getenv("TOOLKITS_DIR") | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| # Try using saved directory | ||||||||||||||||||||||||||||||||||||||||||
| if toolkits_dir and os.path.isdir(toolkits_dir): | ||||||||||||||||||||||||||||||||||||||||||
| if not interactive: | ||||||||||||||||||||||||||||||||||||||||||
| return toolkits_dir | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| use_saved = inquirer.confirm( | ||||||||||||||||||||||||||||||||||||||||||
| message=f"Use saved toolkits directory: {toolkits_dir}?", | ||||||||||||||||||||||||||||||||||||||||||
| default=True, | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -98,6 +115,10 @@ def get_toolkits_dir() -> str: | |||||||||||||||||||||||||||||||||||||||||
| return toolkits_dir | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| # Fall back to manual entry | ||||||||||||||||||||||||||||||||||||||||||
| if not interactive: | ||||||||||||||||||||||||||||||||||||||||||
| console.print(f"❌ No toolkits directories found. Please provide --toolkits-dir or set TOOLKITS_DIR environment variable.", style="bold red") | ||||||||||||||||||||||||||||||||||||||||||
| sys.exit(1) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| console.print("\n[yellow]No toolkits directories found automatically.[/yellow]") | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| while True: | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -156,13 +177,36 @@ def get_available_toolkits(toolkits_dir: str) -> list[str]: | |||||||||||||||||||||||||||||||||||||||||
| return available_toolkits | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| def get_selected_toolkit(console: Console) -> tuple[str, str] | None: | ||||||||||||||||||||||||||||||||||||||||||
| def get_selected_toolkit( | ||||||||||||||||||||||||||||||||||||||||||
| console: Console, | ||||||||||||||||||||||||||||||||||||||||||
| toolkit_path: Optional[str] = None, | ||||||||||||||||||||||||||||||||||||||||||
| toolkit_name: Optional[str] = None, | ||||||||||||||||||||||||||||||||||||||||||
| toolkits_dir: Optional[str] = None, | ||||||||||||||||||||||||||||||||||||||||||
| interactive: bool = True, | ||||||||||||||||||||||||||||||||||||||||||
| ) -> Optional[Tuple[str, str]]: | ||||||||||||||||||||||||||||||||||||||||||
| """Prompt user to select a toolkit from the configured toolkits directory. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||||||||||||||||||
| console: Rich console for output. | ||||||||||||||||||||||||||||||||||||||||||
| toolkit_path: Direct path to a specific toolkit (if provided, use this). | ||||||||||||||||||||||||||||||||||||||||||
| toolkit_name: Name of the toolkit (if provided with toolkits_dir, use this). | ||||||||||||||||||||||||||||||||||||||||||
| toolkits_dir: Parent directory containing toolkits. | ||||||||||||||||||||||||||||||||||||||||||
| interactive: Whether to use interactive prompts. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Returns: | ||||||||||||||||||||||||||||||||||||||||||
| Tuple of (toolkit_dir, toolkit_name) if successful, None otherwise. | ||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||
| toolkits_dir = get_toolkits_dir() | ||||||||||||||||||||||||||||||||||||||||||
| # If toolkit_path is provided directly, use it | ||||||||||||||||||||||||||||||||||||||||||
| if toolkit_path: | ||||||||||||||||||||||||||||||||||||||||||
| if not os.path.isdir(toolkit_path): | ||||||||||||||||||||||||||||||||||||||||||
| console.print(f"❌ Toolkit path does not exist: {toolkit_path}", style="bold red") | ||||||||||||||||||||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||||||||||||||||||||
| toolkit_name_from_path = Path(toolkit_path).name | ||||||||||||||||||||||||||||||||||||||||||
| return (toolkit_path, toolkit_name_from_path) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| # Otherwise, get toolkits directory | ||||||||||||||||||||||||||||||||||||||||||
| if not toolkits_dir: | ||||||||||||||||||||||||||||||||||||||||||
| toolkits_dir = get_toolkits_dir(interactive=interactive) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| # Get list of available toolkits in the directory | ||||||||||||||||||||||||||||||||||||||||||
| available_toolkits = get_available_toolkits(toolkits_dir) | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -171,12 +215,29 @@ def get_selected_toolkit(console: Console) -> tuple[str, str] | None: | |||||||||||||||||||||||||||||||||||||||||
| console.print(f"❌ No valid toolkits found in {toolkits_dir}", style="bold red") | ||||||||||||||||||||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| # Ask user to select a toolkit with fuzzy search | ||||||||||||||||||||||||||||||||||||||||||
| selected_toolkit = inquirer.fuzzy( | ||||||||||||||||||||||||||||||||||||||||||
| message="Select a toolkit (type to filter):", | ||||||||||||||||||||||||||||||||||||||||||
| choices=sorted(available_toolkits), | ||||||||||||||||||||||||||||||||||||||||||
| max_height="70%", | ||||||||||||||||||||||||||||||||||||||||||
| ).execute() | ||||||||||||||||||||||||||||||||||||||||||
| # If toolkit_name is provided, use it directly | ||||||||||||||||||||||||||||||||||||||||||
| if toolkit_name: | ||||||||||||||||||||||||||||||||||||||||||
| if toolkit_name not in available_toolkits: | ||||||||||||||||||||||||||||||||||||||||||
| console.print( | ||||||||||||||||||||||||||||||||||||||||||
| f"❌ Toolkit '{toolkit_name}' not found in {toolkits_dir}. " | ||||||||||||||||||||||||||||||||||||||||||
| f"Available toolkits: {', '.join(sorted(available_toolkits))}", | ||||||||||||||||||||||||||||||||||||||||||
| style="bold red", | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||||||||||||||||||||
| selected_toolkit = toolkit_name | ||||||||||||||||||||||||||||||||||||||||||
| elif interactive: | ||||||||||||||||||||||||||||||||||||||||||
| # Ask user to select a toolkit with fuzzy search | ||||||||||||||||||||||||||||||||||||||||||
| selected_toolkit = inquirer.fuzzy( | ||||||||||||||||||||||||||||||||||||||||||
| message="Select a toolkit (type to filter):", | ||||||||||||||||||||||||||||||||||||||||||
| choices=sorted(available_toolkits), | ||||||||||||||||||||||||||||||||||||||||||
| max_height="70%", | ||||||||||||||||||||||||||||||||||||||||||
| ).execute() | ||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||
| console.print( | ||||||||||||||||||||||||||||||||||||||||||
| f"❌ Must provide --toolkit-name or --toolkit-path when running non-interactively", | ||||||||||||||||||||||||||||||||||||||||||
| style="bold red", | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| toolkits_path = Path(toolkits_dir) | ||||||||||||||||||||||||||||||||||||||||||
| server_dir = str(toolkits_path / selected_toolkit) | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -185,56 +246,118 @@ def get_selected_toolkit(console: Console) -> tuple[str, str] | None: | |||||||||||||||||||||||||||||||||||||||||
| return (server_dir, server_name) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| def run() -> None: | ||||||||||||||||||||||||||||||||||||||||||
| def run( | ||||||||||||||||||||||||||||||||||||||||||
| toolkit_path: Optional[str] = typer.Option(None, "--toolkit-path", "-p", help="Direct path to the toolkit directory"), | ||||||||||||||||||||||||||||||||||||||||||
| toolkit_name: Optional[str] = typer.Option(None, "--toolkit-name", "-n", help="Name of the toolkit (requires --toolkits-dir)"), | ||||||||||||||||||||||||||||||||||||||||||
| toolkits_dir: Optional[str] = typer.Option(None, "--toolkits-dir", "-d", help="Path to directory containing multiple toolkits"), | ||||||||||||||||||||||||||||||||||||||||||
| docs_section: Optional[str] = typer.Option(None, "--docs-section", "-s", help="Documentation section (e.g., 'productivity', 'development')"), | ||||||||||||||||||||||||||||||||||||||||||
| openai_api_key: Optional[str] = typer.Option(None, "--openai-api-key", "-k", help="OpenAI API key (or set OPENAI_API_KEY env var)"), | ||||||||||||||||||||||||||||||||||||||||||
| skip_examples: bool = typer.Option(False, "--skip-examples", help="Skip generating tool call examples"), | ||||||||||||||||||||||||||||||||||||||||||
| max_concurrency: int = typer.Option(5, "--max-concurrency", "-c", help="Max concurrency for OpenAI API requests"), | ||||||||||||||||||||||||||||||||||||||||||
| ) -> None: | ||||||||||||||||||||||||||||||||||||||||||
| """Interactive command to generate documentation for a server.""" | ||||||||||||||||||||||||||||||||||||||||||
| load_dotenv(ENV_FILE) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| # Determine if we're running interactively | ||||||||||||||||||||||||||||||||||||||||||
| interactive = not any([toolkit_path, toolkit_name, toolkits_dir, docs_section]) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
| # Determine if we're running interactively | |
| interactive = not any([toolkit_path, toolkit_name, toolkits_dir, docs_section]) | |
| # Determine if we're running interactively. | |
| # Non-interactive mode is only enabled when *all* selection arguments are provided. | |
| selection_args_provided = [ | |
| toolkit_path is not None, | |
| toolkit_name is not None, | |
| toolkits_dir is not None, | |
| docs_section is not None, | |
| ] | |
| all_selection_args_provided = all(selection_args_provided) | |
| any_selection_arg_provided = any(selection_args_provided) | |
| if any_selection_arg_provided and not all_selection_args_provided: | |
| console.print( | |
| "[yellow]Some CLI arguments were provided, but not all required for " | |
| "non-interactive mode. Falling back to interactive mode.[/yellow]" | |
| ) | |
| interactive = not all_selection_args_provided |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Toolkits dir option disables interactive prompts
interactive is inferred as false whenever --toolkits-dir (or a few other flags) is provided, which stops prompting for missing inputs and instead errors in get_selected_toolkit unless --toolkit-name/--toolkit-path (and --docs-section) are also provided. This breaks the common “set base dir but still choose interactively” flow.
Outdated
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 335 already includes server_name in the list, so the conditional append on line 337 will create a duplicate. The condition on line 336 should check if actual_toolkit_name == server_name to avoid adding server_name twice, or simply remove the conditional append since server_name is already in the list.
| toolkit_names_to_try = [actual_toolkit_name, server_name] | |
| toolkit_names_to_try = [actual_toolkit_name] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Non-interactive mode still prompts on discovery
When
interactive=False,get_toolkits_dirstill callsinquirer.selectif multiple toolkits directories are discovered. This can hang or fail in CI/non-TTY runs where non-interactive behavior is expected.