evalstate HF Staff commited on
Commit
c830f69
·
verified ·
1 Parent(s): bcee6cb

Deploy hf-hub-query with runtime capabilities helper and budget prompt fix

Browse files
Files changed (3) hide show
  1. _monty_codegen_shared.md +14 -0
  2. hf-hub-query.md +1 -0
  3. monty_api_tool_v2.py +212 -0
_monty_codegen_shared.md CHANGED
@@ -18,6 +18,7 @@ await solve(query, max_calls)
18
  - no `await solve(query, max_calls if ... else ...)`
19
  - no `budget = max_calls` followed by `await solve(query, budget)`
20
  - The runtime supplies `max_calls`; generated code must not invent defaults or fallbacks for it.
 
21
  - Use helper functions first. Use raw `call_api('/api/...')` only if no helper fits.
22
  - `call_api` must receive a raw path starting with `/api/...`; never call helper names through `call_api`.
23
  - Raw `call_api(...)` endpoints must match the runtime allowlist exactly. Do **not** invent hyphen/underscore variants or guessed path shapes.
@@ -25,6 +26,7 @@ await solve(query, max_calls)
25
  - `call_api(...)` only accepts `endpoint`, `params`, `method`, and `json_body`. Do not guess extra kwargs.
26
  - Use `call_api(...)` only for endpoint families that do not already have a helper, such as `/api/daily_papers` or tag metadata endpoints.
27
  - For daily papers, use the exact raw endpoint string `/api/daily_papers` (underscore), **not** `/api/daily-papers`.
 
28
  - Keep final displayed results compact, but do not artificially shrink intermediate helper coverage unless the user explicitly asked for a sample.
29
  - Prefer canonical snake_case keys in generated code and in JSON output.
30
  - When returning a structured dict that includes your own coverage metadata, use the exact top-level keys `results` and `coverage` unless the user explicitly requested different key names.
@@ -63,6 +65,9 @@ Rules:
63
  - For bounded list/sample helpers in raw mode, returning the helper envelope directly preserves helper-owned `meta` fields.
64
 
65
  ## Routing guide
 
 
 
66
  ### Repo questions
67
  - Exact `owner/name` details → `hf_repo_details(repo_type="auto", ...)`
68
  - Search/discovery/list/top repos → `hf_repo_search(...)`
@@ -173,6 +178,8 @@ Common aliases tolerated in `fields=[...]`:
173
 
174
  ## Helper API
175
  ```py
 
 
176
  await hf_org_overview(organization: str)
177
 
178
  await hf_org_members(
@@ -310,6 +317,13 @@ return {
310
  "repo_url": item.get("repo_url"),
311
  }
312
 
 
 
 
 
 
 
 
313
  # Compact user summary
314
  summary = await hf_user_summary(
315
  username="mishig",
 
18
  - no `await solve(query, max_calls if ... else ...)`
19
  - no `budget = max_calls` followed by `await solve(query, budget)`
20
  - The runtime supplies `max_calls`; generated code must not invent defaults or fallbacks for it.
21
+ - At the tool-call layer, normally omit `max_calls` and `timeout_sec` so the runtime defaults apply. Do **not** invent small explicit tool-call budgets like `10` or `20` for ordinary requests.
22
  - Use helper functions first. Use raw `call_api('/api/...')` only if no helper fits.
23
  - `call_api` must receive a raw path starting with `/api/...`; never call helper names through `call_api`.
24
  - Raw `call_api(...)` endpoints must match the runtime allowlist exactly. Do **not** invent hyphen/underscore variants or guessed path shapes.
 
26
  - `call_api(...)` only accepts `endpoint`, `params`, `method`, and `json_body`. Do not guess extra kwargs.
27
  - Use `call_api(...)` only for endpoint families that do not already have a helper, such as `/api/daily_papers` or tag metadata endpoints.
28
  - For daily papers, use the exact raw endpoint string `/api/daily_papers` (underscore), **not** `/api/daily-papers`.
29
+ - For questions about supported helpers, fields, limits, raw API affordances, or runtime capabilities, use `hf_runtime_capabilities(...)` instead of hand-authoring a static answer from memory.
30
  - Keep final displayed results compact, but do not artificially shrink intermediate helper coverage unless the user explicitly asked for a sample.
31
  - Prefer canonical snake_case keys in generated code and in JSON output.
32
  - When returning a structured dict that includes your own coverage metadata, use the exact top-level keys `results` and `coverage` unless the user explicitly requested different key names.
 
65
  - For bounded list/sample helpers in raw mode, returning the helper envelope directly preserves helper-owned `meta` fields.
66
 
67
  ## Routing guide
68
+ ### Runtime self-description
69
+ - Supported fields / helper signatures / limits / raw API affordances → `hf_runtime_capabilities(...)`
70
+
71
  ### Repo questions
72
  - Exact `owner/name` details → `hf_repo_details(repo_type="auto", ...)`
73
  - Search/discovery/list/top repos → `hf_repo_search(...)`
 
178
 
179
  ## Helper API
180
  ```py
181
+ await hf_runtime_capabilities(section: str | None = None)
182
+
183
  await hf_org_overview(organization: str)
184
 
185
  await hf_org_members(
 
317
  "repo_url": item.get("repo_url"),
318
  }
319
 
320
+ # Runtime capability / supported-field introspection
321
+ caps = await hf_runtime_capabilities(section="fields")
322
+ if not caps["ok"]:
323
+ return caps
324
+ item = caps["item"] or (caps["items"][0] if caps["items"] else None)
325
+ return item["content"]
326
+
327
  # Compact user summary
328
  summary = await hf_user_summary(
329
  username="mishig",
hf-hub-query.md CHANGED
@@ -25,6 +25,7 @@ The user must never see your generated Python unless they explicitly ask for deb
25
  - Only ask a brief clarification question if the request is genuinely ambiguous or missing required identity.
26
  - The generated program must define `async def solve(query, max_calls): ...` and end with `await solve(query, max_calls)`.
27
  - Use the original user request, or a tight restatement, as the tool `query`.
 
28
  - One user request = one `hf_hub_query_raw` call. Do **not** retry in the same turn.
29
 
30
  ## Raw return rules
 
25
  - Only ask a brief clarification question if the request is genuinely ambiguous or missing required identity.
26
  - The generated program must define `async def solve(query, max_calls): ...` and end with `await solve(query, max_calls)`.
27
  - Use the original user request, or a tight restatement, as the tool `query`.
28
+ - Do **not** pass explicit `max_calls` or `timeout_sec` tool arguments unless the user explicitly asked for a non-default budget/timeout. Let the runtime defaults apply for ordinary requests.
29
  - One user request = one `hf_hub_query_raw` call. Do **not** retry in the same turn.
30
 
31
  ## Raw return rules
monty_api_tool_v2.py CHANGED
@@ -14,6 +14,7 @@ import argparse
14
  import asyncio
15
  import ast
16
  import contextvars
 
17
  import json
18
  import os
19
  import re
@@ -152,6 +153,68 @@ _COLLECTION_FIELD_ALIASES: dict[str, str] = {
152
  "author": "owner",
153
  }
154
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  # Extra hf_repo_search kwargs intentionally supported as pass-through to
156
  # huggingface_hub.HfApi.list_models/list_datasets/list_spaces.
157
  # (Generic args like `query/search/sort/author/limit` are handled directly in
@@ -278,6 +341,7 @@ PAGINATION_POLICY: dict[str, dict[str, Any]] = {
278
  # Single source of truth for the public helper surface exposed to generated
279
  # Monty code. Keep runtime helper resolution derived from this tuple.
280
  HELPER_EXTERNALS = (
 
281
  "hf_whoami",
282
  "hf_org_overview",
283
  "hf_org_members",
@@ -1070,6 +1134,7 @@ async def _run_with_monty(
1070
  trace: list[dict[str, Any]] = []
1071
  limit_summaries: list[dict[str, Any]] = []
1072
  latest_helper_error: dict[str, Any] | None = None
 
1073
  def _budget_remaining() -> int:
1074
  return max(0, max_calls - call_count["n"])
1075
 
@@ -3500,6 +3565,146 @@ async def _run_with_monty(
3500
  repo_types=sorted(allowed_repo_types) if allowed_repo_types is not None else None,
3501
  )
3502
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3503
  m = pydantic_monty.Monty(
3504
  code,
3505
  inputs=["query", "max_calls"],
@@ -3545,6 +3750,13 @@ async def _run_with_monty(
3545
  # code either returns that explicit helper error envelope or flattens it
3546
  # into an empty fallback shape, preserve the helper-owned error instead
3547
  # of replacing it with a generic zero-call runtime failure.
 
 
 
 
 
 
 
3548
  if latest_helper_error is not None:
3549
  return {"output": _truncate_result_payload(latest_helper_error), "api_calls": call_count["n"], "trace": trace, "limit_summaries": limit_summaries}
3550
  if isinstance(result, dict) and result.get("ok") is False and isinstance(result.get("error"), str):
 
14
  import asyncio
15
  import ast
16
  import contextvars
17
+ import inspect
18
  import json
19
  import os
20
  import re
 
153
  "author": "owner",
154
  }
155
 
156
+ REPO_CANONICAL_FIELDS: tuple[str, ...] = (
157
+ "repo_id",
158
+ "repo_type",
159
+ "title",
160
+ "author",
161
+ "likes",
162
+ "downloads",
163
+ "created_at",
164
+ "last_modified",
165
+ "pipeline_tag",
166
+ "repo_url",
167
+ "tags",
168
+ "library_name",
169
+ "description",
170
+ "paperswithcode_id",
171
+ "sdk",
172
+ "models",
173
+ "datasets",
174
+ "subdomain",
175
+ )
176
+
177
+ USER_CANONICAL_FIELDS: tuple[str, ...] = (
178
+ "username",
179
+ "fullname",
180
+ "bio",
181
+ "websiteUrl",
182
+ "twitter",
183
+ "github",
184
+ "linkedin",
185
+ "bluesky",
186
+ "followers",
187
+ "following",
188
+ "likes",
189
+ "isPro",
190
+ )
191
+
192
+ ACTOR_CANONICAL_FIELDS: tuple[str, ...] = (
193
+ "username",
194
+ "fullname",
195
+ "isPro",
196
+ "role",
197
+ "type",
198
+ )
199
+
200
+ ACTIVITY_CANONICAL_FIELDS: tuple[str, ...] = (
201
+ "event_type",
202
+ "repo_id",
203
+ "repo_type",
204
+ "timestamp",
205
+ )
206
+
207
+ COLLECTION_CANONICAL_FIELDS: tuple[str, ...] = (
208
+ "collection_id",
209
+ "slug",
210
+ "title",
211
+ "owner",
212
+ "owner_type",
213
+ "description",
214
+ "last_updated",
215
+ "item_count",
216
+ )
217
+
218
  # Extra hf_repo_search kwargs intentionally supported as pass-through to
219
  # huggingface_hub.HfApi.list_models/list_datasets/list_spaces.
220
  # (Generic args like `query/search/sort/author/limit` are handled directly in
 
341
  # Single source of truth for the public helper surface exposed to generated
342
  # Monty code. Keep runtime helper resolution derived from this tuple.
343
  HELPER_EXTERNALS = (
344
+ "hf_runtime_capabilities",
345
  "hf_whoami",
346
  "hf_org_overview",
347
  "hf_org_members",
 
1134
  trace: list[dict[str, Any]] = []
1135
  limit_summaries: list[dict[str, Any]] = []
1136
  latest_helper_error: dict[str, Any] | None = None
1137
+ internal_helper_used = {"used": False}
1138
  def _budget_remaining() -> int:
1139
  return max(0, max_calls - call_count["n"])
1140
 
 
3565
  repo_types=sorted(allowed_repo_types) if allowed_repo_types is not None else None,
3566
  )
3567
 
3568
+ async def hf_runtime_capabilities(section: str | None = None) -> dict[str, Any]:
3569
+ start_calls = call_count["n"]
3570
+ internal_helper_used["used"] = True
3571
+
3572
+ def _render_annotation(annotation: Any) -> str:
3573
+ if annotation is inspect.Signature.empty:
3574
+ return "Any"
3575
+ return str(annotation)
3576
+
3577
+ def _render_default(default: Any) -> str | None:
3578
+ if default is inspect.Signature.empty:
3579
+ return None
3580
+ return repr(default)
3581
+
3582
+ def _signature_payload(fn: Callable[..., Any]) -> dict[str, Any]:
3583
+ signature = inspect.signature(fn)
3584
+ parameters: list[dict[str, Any]] = []
3585
+ for parameter in signature.parameters.values():
3586
+ item: dict[str, Any] = {
3587
+ "name": parameter.name,
3588
+ "kind": str(parameter.kind).replace("Parameter.", "").lower(),
3589
+ "annotation": _render_annotation(parameter.annotation),
3590
+ "required": parameter.default is inspect.Signature.empty,
3591
+ }
3592
+ default = _render_default(parameter.default)
3593
+ if default is not None:
3594
+ item["default"] = default
3595
+ parameters.append(item)
3596
+ return {
3597
+ "parameters": parameters,
3598
+ "returns": _render_annotation(signature.return_annotation),
3599
+ }
3600
+
3601
+ helper_payload = {
3602
+ name: _signature_payload(fn)
3603
+ for name, fn in sorted(helper_functions.items())
3604
+ }
3605
+
3606
+ manifest: dict[str, Any] = {
3607
+ "overview": {
3608
+ "helper_count": len(helper_functions),
3609
+ "supports_current_user": True,
3610
+ "supports_raw_api_fallback": True,
3611
+ "helper_result_envelope": {
3612
+ "ok": "bool",
3613
+ "item": "dict | None",
3614
+ "items": "list[dict]",
3615
+ "meta": "dict",
3616
+ "error": "str | None",
3617
+ },
3618
+ "raw_result_envelope": {
3619
+ "result": "Any",
3620
+ "meta": {
3621
+ "ok": "bool",
3622
+ "api_calls": "int",
3623
+ "elapsed_ms": "int",
3624
+ "limits_reached": "bool",
3625
+ "limit_summary": "list[dict]",
3626
+ },
3627
+ },
3628
+ },
3629
+ "helpers": helper_payload,
3630
+ "fields": {
3631
+ "repo": list(REPO_CANONICAL_FIELDS),
3632
+ "user": list(USER_CANONICAL_FIELDS),
3633
+ "actor": list(ACTOR_CANONICAL_FIELDS),
3634
+ "activity": list(ACTIVITY_CANONICAL_FIELDS),
3635
+ "collection": list(COLLECTION_CANONICAL_FIELDS),
3636
+ },
3637
+ "aliases": {
3638
+ "repo": dict(sorted(_REPO_FIELD_ALIASES.items())),
3639
+ "user": dict(sorted(_USER_FIELD_ALIASES.items())),
3640
+ "actor": dict(sorted(_ACTOR_FIELD_ALIASES.items())),
3641
+ "collection": dict(sorted(_COLLECTION_FIELD_ALIASES.items())),
3642
+ "sort_keys": dict(sorted(_SORT_KEY_ALIASES.items())),
3643
+ },
3644
+ "limits": {
3645
+ "default_timeout_sec": DEFAULT_TIMEOUT_SEC,
3646
+ "default_max_calls": DEFAULT_MAX_CALLS,
3647
+ "max_calls_limit": MAX_CALLS_LIMIT,
3648
+ "output_items_truncation_limit": OUTPUT_ITEMS_TRUNCATION_LIMIT,
3649
+ "graph_scan_limit_cap": GRAPH_SCAN_LIMIT_CAP,
3650
+ "likes_scan_limit_cap": LIKES_SCAN_LIMIT_CAP,
3651
+ "recent_activity_scan_max_pages": RECENT_ACTIVITY_SCAN_MAX_PAGES,
3652
+ "trending_endpoint_max_limit": TRENDING_ENDPOINT_MAX_LIMIT,
3653
+ "pagination_policy": {
3654
+ helper_name: dict(sorted(policy.items()))
3655
+ for helper_name, policy in sorted(PAGINATION_POLICY.items())
3656
+ },
3657
+ },
3658
+ "raw_api": {
3659
+ "call_api": _signature_payload(call_api),
3660
+ "allowed_methods": ["GET", "POST"],
3661
+ "allowed_endpoint_patterns": list(ALLOWLIST_PATTERNS),
3662
+ "helper_covered_endpoint_patterns": [
3663
+ {"pattern": pattern, "helper": helper_name}
3664
+ for pattern, helper_name in HELPER_COVERED_ENDPOINT_PATTERNS
3665
+ ],
3666
+ },
3667
+ "repo_search": {
3668
+ "sort_keys": {
3669
+ repo_type: sorted(keys)
3670
+ for repo_type, keys in sorted(_REPO_SORT_KEYS.items())
3671
+ },
3672
+ "extra_args": {
3673
+ repo_type: sorted(args)
3674
+ for repo_type, args in sorted(_REPO_SEARCH_EXTRA_ARGS.items())
3675
+ },
3676
+ },
3677
+ }
3678
+
3679
+ allowed_sections = sorted(manifest)
3680
+ requested = str(section or "").strip().lower()
3681
+ if requested:
3682
+ if requested not in manifest:
3683
+ return _helper_error(
3684
+ start_calls=start_calls,
3685
+ source="internal://runtime-capabilities",
3686
+ error=f"Unsupported section {section!r}. Allowed sections: {allowed_sections}",
3687
+ section=section,
3688
+ allowed_sections=allowed_sections,
3689
+ )
3690
+ payload = {
3691
+ "section": requested,
3692
+ "content": manifest[requested],
3693
+ "allowed_sections": allowed_sections,
3694
+ }
3695
+ else:
3696
+ payload = {
3697
+ "allowed_sections": allowed_sections,
3698
+ **manifest,
3699
+ }
3700
+
3701
+ return _helper_success(
3702
+ start_calls=start_calls,
3703
+ source="internal://runtime-capabilities",
3704
+ items=[payload],
3705
+ section=requested or None,
3706
+ )
3707
+
3708
  m = pydantic_monty.Monty(
3709
  code,
3710
  inputs=["query", "max_calls"],
 
3750
  # code either returns that explicit helper error envelope or flattens it
3751
  # into an empty fallback shape, preserve the helper-owned error instead
3752
  # of replacing it with a generic zero-call runtime failure.
3753
+ if internal_helper_used["used"]:
3754
+ return {"output": _truncate_result_payload(result), "api_calls": call_count["n"], "trace": trace, "limit_summaries": limit_summaries}
3755
+ if isinstance(result, dict) and result.get("ok") is True:
3756
+ meta = result.get("meta") if isinstance(result.get("meta"), dict) else {}
3757
+ source = meta.get("source")
3758
+ if isinstance(source, str) and source.startswith("internal://"):
3759
+ return {"output": _truncate_result_payload(result), "api_calls": call_count["n"], "trace": trace, "limit_summaries": limit_summaries}
3760
  if latest_helper_error is not None:
3761
  return {"output": _truncate_result_payload(latest_helper_error), "api_calls": call_count["n"], "trace": trace, "limit_summaries": limit_summaries}
3762
  if isinstance(result, dict) and result.get("ok") is False and isinstance(result.get("error"), str):