Skip to content

Commit 072be4c

Browse files
yeldarbyclaude
andcommitted
feat(cli): add model infer, unify image search, fix description truncation
1. Add 'model infer' command that delegates to the existing infer logic. Now inference lives in the noun-verb hierarchy where users expect it. 2. Make 'image search' project-optional: without -p searches the whole workspace (using the workspace-level RoboQL search). With -p searches within a specific project. Also adds --export for dataset export. This unifies the old 'search' and 'image search' into one command. 3. Fix help text truncation by using cmd.help (full docstring first line) instead of short_help which Click caps at ~45 chars. Hidden aliases (roboflow infer, roboflow search, roboflow download) all still work — they're just not shown in --help. 374 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c217b7a commit 072be4c

3 files changed

Lines changed: 85 additions & 7 deletions

File tree

roboflow/cli/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,10 @@ def _walk(group: Any, prefix: str = "") -> None:
110110
if hasattr(cmd, "list_commands") and cmd.list_commands(None):
111111
_walk(cmd, full)
112112
else:
113-
# Use the full short_help (not truncated get_short_help_str)
114-
help_text = getattr(cmd, "short_help", None) or cmd.get_short_help_str() or ""
113+
# Use the full help text: try help attr, then short_help, then docstring
114+
help_text = getattr(cmd, "help", None) or getattr(cmd, "short_help", None) or ""
115+
# Take only the first line/sentence
116+
help_text = help_text.split("\n")[0].strip()
115117
commands.append((full, help_text))
116118

117119
_walk(click_app)

roboflow/cli/handlers/image.py

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,71 @@ def get_image(
6262
@image_app.command("search")
6363
def search_images(
6464
ctx: typer.Context,
65-
query: Annotated[str, typer.Argument(help="RoboQL search query")],
66-
project: Annotated[str, typer.Option("-p", "--project", help="Project ID (used in query filter)")],
65+
query: Annotated[str, typer.Argument(help="RoboQL search query (e.g. 'tag:review' or '*')")],
66+
project: Annotated[
67+
Optional[str], typer.Option("-p", "--project", help="Project ID (omit to search entire workspace)")
68+
] = None,
6769
limit: Annotated[int, typer.Option(help="Number of results")] = 50,
6870
cursor: Annotated[Optional[str], typer.Option(help="Continuation token for pagination")] = None,
71+
export: Annotated[bool, typer.Option("--export", help="Export search results as a dataset")] = False,
72+
format: Annotated[str, typer.Option("-f", "--format", help="Annotation format for export")] = "coco",
73+
location: Annotated[Optional[str], typer.Option("-l", "--location", help="Local directory for export")] = None,
74+
dataset: Annotated[
75+
Optional[str], typer.Option("-d", "--dataset", help="Limit export to a specific dataset")
76+
] = None,
77+
annotation_group: Annotated[
78+
Optional[str], typer.Option("-g", "--annotation-group", help="Annotation group")
79+
] = None,
80+
name: Annotated[Optional[str], typer.Option(help="Optional name for the export")] = None,
81+
no_extract: Annotated[bool, typer.Option("--no-extract", help="Keep zip file, skip extraction")] = False,
6982
) -> None:
70-
"""Search images in workspace."""
71-
args = ctx_to_args(ctx, query=query, project=project, limit=limit, cursor=cursor)
72-
_handle_search(args)
83+
"""Search images in workspace or project.
84+
85+
Without -p/--project, searches across the entire workspace using RoboQL.
86+
With -p/--project, searches within a specific project.
87+
Use --export to download matching results as a dataset.
88+
"""
89+
if project:
90+
# Project-scoped search (legacy behavior)
91+
args = ctx_to_args(ctx, query=query, project=project, limit=limit, cursor=cursor)
92+
_handle_search(args)
93+
elif export:
94+
# Workspace-level export
95+
from roboflow.cli.handlers.search import _search
96+
97+
args = ctx_to_args(
98+
ctx,
99+
query=query,
100+
limit=limit,
101+
cursor=cursor,
102+
export=True,
103+
format=format,
104+
location=location,
105+
dataset=dataset,
106+
annotation_group=annotation_group,
107+
name=name,
108+
no_extract=no_extract,
109+
)
110+
_search(args)
111+
else:
112+
# Workspace-level search
113+
from roboflow.cli.handlers.search import _search
114+
115+
args = ctx_to_args(
116+
ctx,
117+
query=query,
118+
limit=limit,
119+
cursor=cursor,
120+
export=False,
121+
format=format,
122+
location=location,
123+
dataset=dataset,
124+
annotation_group=annotation_group,
125+
name=name,
126+
no_extract=no_extract,
127+
fields=None,
128+
)
129+
_search(args)
73130

74131

75132
@image_app.command("tag")

roboflow/cli/handlers/model.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,25 @@ def get_model(
3131
_get_model(args)
3232

3333

34+
@model_app.command("infer")
35+
def model_infer(
36+
ctx: typer.Context,
37+
file: Annotated[str, typer.Argument(help="Path to an image file")],
38+
model: Annotated[str, typer.Option("-m", "--model", help="Model ID (project/version, e.g. my-project/3)")],
39+
confidence: Annotated[float, typer.Option("-c", "--confidence", help="Confidence threshold 0.0-1.0")] = 0.5,
40+
overlap: Annotated[float, typer.Option("-o", "--overlap", help="Overlap/NMS threshold 0.0-1.0")] = 0.5,
41+
type: Annotated[
42+
Optional[str],
43+
typer.Option("-t", "--type", help="Model type (auto-detected if not specified)"),
44+
] = None,
45+
) -> None:
46+
"""Run inference on an image using a trained model."""
47+
from roboflow.cli.handlers.infer import _infer
48+
49+
args = ctx_to_args(ctx, file=file, model=model, confidence=confidence, overlap=overlap, type=type)
50+
_infer(args)
51+
52+
3453
@model_app.command("upload")
3554
def upload_model(
3655
ctx: typer.Context,

0 commit comments

Comments
 (0)