huggingface-hub 0.32.2__py3-none-any.whl → 0.32.4__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.

Potentially problematic release.


This version of huggingface-hub might be problematic. Click here for more details.

@@ -46,7 +46,7 @@ import sys
46
46
  from typing import TYPE_CHECKING
47
47
 
48
48
 
49
- __version__ = "0.32.2"
49
+ __version__ = "0.32.4"
50
50
 
51
51
  # Alphabetical order of definitions is ensured in tests
52
52
  # WARNING: any comment added in this dictionary definition will be lost when
@@ -1502,7 +1502,7 @@ class InferenceClient:
1502
1502
  model_id = model or self.model
1503
1503
  provider_helper = get_provider_helper(self.provider, task="question-answering", model=model_id)
1504
1504
  request_parameters = provider_helper.prepare_request(
1505
- inputs=None,
1505
+ inputs={"question": question, "context": context},
1506
1506
  parameters={
1507
1507
  "align_to_words": align_to_words,
1508
1508
  "doc_stride": doc_stride,
@@ -1512,7 +1512,6 @@ class InferenceClient:
1512
1512
  "max_seq_len": max_seq_len,
1513
1513
  "top_k": top_k,
1514
1514
  },
1515
- extra_payload={"question": question, "context": context},
1516
1515
  headers=self.headers,
1517
1516
  model=model_id,
1518
1517
  api_key=self.token,
@@ -1551,7 +1551,7 @@ class AsyncInferenceClient:
1551
1551
  model_id = model or self.model
1552
1552
  provider_helper = get_provider_helper(self.provider, task="question-answering", model=model_id)
1553
1553
  request_parameters = provider_helper.prepare_request(
1554
- inputs=None,
1554
+ inputs={"question": question, "context": context},
1555
1555
  parameters={
1556
1556
  "align_to_words": align_to_words,
1557
1557
  "doc_stride": doc_stride,
@@ -1561,7 +1561,6 @@ class AsyncInferenceClient:
1561
1561
  "max_seq_len": max_seq_len,
1562
1562
  "top_k": top_k,
1563
1563
  },
1564
- extra_payload={"question": question, "context": context},
1565
1564
  headers=self.headers,
1566
1565
  model=model_id,
1567
1566
  api_key=self.token,
@@ -0,0 +1,88 @@
1
+ import asyncio
2
+ import sys
3
+ from functools import partial
4
+
5
+ import typer
6
+
7
+
8
+ def _patch_anyio_open_process():
9
+ """
10
+ Patch anyio.open_process to allow detached processes on Windows and Unix-like systems.
11
+
12
+ This is necessary to prevent the MCP client from being interrupted by Ctrl+C when running in the CLI.
13
+ """
14
+ import subprocess
15
+
16
+ import anyio
17
+
18
+ if getattr(anyio, "_tiny_agents_patched", False):
19
+ return
20
+ anyio._tiny_agents_patched = True
21
+
22
+ original_open_process = anyio.open_process
23
+
24
+ if sys.platform == "win32":
25
+ # On Windows, we need to set the creation flags to create a new process group
26
+
27
+ async def open_process_in_new_group(*args, **kwargs):
28
+ """
29
+ Wrapper for open_process to handle Windows-specific process creation flags.
30
+ """
31
+ # Ensure we pass the creation flags for Windows
32
+ kwargs.setdefault("creationflags", subprocess.CREATE_NEW_PROCESS_GROUP)
33
+ return await original_open_process(*args, **kwargs)
34
+
35
+ anyio.open_process = open_process_in_new_group
36
+ else:
37
+ # For Unix-like systems, we can use setsid to create a new session
38
+ async def open_process_in_new_group(*args, **kwargs):
39
+ """
40
+ Wrapper for open_process to handle Unix-like systems with start_new_session=True.
41
+ """
42
+ kwargs.setdefault("start_new_session", True)
43
+ return await original_open_process(*args, **kwargs)
44
+
45
+ anyio.open_process = open_process_in_new_group
46
+
47
+
48
+ async def _async_prompt(exit_event: asyncio.Event, prompt: str = "» ") -> str:
49
+ """
50
+ Asynchronous prompt function that reads input from stdin without blocking.
51
+
52
+ This function is designed to work in an asynchronous context, allowing the event loop to gracefully stop it (e.g. on Ctrl+C).
53
+
54
+ Alternatively, we could use https://github.com/vxgmichel/aioconsole but that would be an additional dependency.
55
+ """
56
+ loop = asyncio.get_event_loop()
57
+
58
+ if sys.platform == "win32":
59
+ # Windows: Use run_in_executor to avoid blocking the event loop
60
+ # Degraded solution: this is not ideal as user will have to CTRL+C once more to stop the prompt (and it'll not be graceful)
61
+ return await loop.run_in_executor(None, partial(typer.prompt, prompt, prompt_suffix=" "))
62
+ else:
63
+ # UNIX-like: Use loop.add_reader for non-blocking stdin read
64
+ future = loop.create_future()
65
+
66
+ def on_input():
67
+ line = sys.stdin.readline()
68
+ loop.remove_reader(sys.stdin)
69
+ future.set_result(line)
70
+
71
+ print(prompt, end=" ", flush=True)
72
+ loop.add_reader(sys.stdin, on_input) # not supported on Windows
73
+
74
+ # Wait for user input or exit event
75
+ # Wait until either the user hits enter or exit_event is set
76
+ exit_task = asyncio.create_task(exit_event.wait())
77
+ await asyncio.wait(
78
+ [future, exit_task],
79
+ return_when=asyncio.FIRST_COMPLETED,
80
+ )
81
+
82
+ # Check which one has been triggered
83
+ if exit_event.is_set():
84
+ future.cancel()
85
+ return ""
86
+
87
+ line = await future
88
+ return line.strip()
@@ -2,12 +2,12 @@ import asyncio
2
2
  import os
3
3
  import signal
4
4
  import traceback
5
- from functools import partial
6
5
  from typing import Any, Dict, List, Optional
7
6
 
8
7
  import typer
9
8
  from rich import print
10
9
 
10
+ from ._cli_hacks import _async_prompt, _patch_anyio_open_process
11
11
  from .agent import Agent
12
12
  from .utils import _load_agent_config
13
13
 
@@ -25,11 +25,6 @@ run_cli = typer.Typer(
25
25
  app.add_typer(run_cli, name="run")
26
26
 
27
27
 
28
- async def _ainput(prompt: str = "» ") -> str:
29
- loop = asyncio.get_running_loop()
30
- return await loop.run_in_executor(None, partial(typer.prompt, prompt, prompt_suffix=" "))
31
-
32
-
33
28
  async def run_agent(
34
29
  agent_path: Optional[str],
35
30
  ) -> None:
@@ -41,11 +36,15 @@ async def run_agent(
41
36
  Path to a local folder containing an `agent.json` and optionally a custom `PROMPT.md` file or a built-in agent stored in a Hugging Face dataset.
42
37
 
43
38
  """
39
+ _patch_anyio_open_process() # Hacky way to prevent stdio connections to be stopped by Ctrl+C
40
+
44
41
  config, prompt = _load_agent_config(agent_path)
45
42
 
43
+ inputs: List[Dict[str, Any]] = config.get("inputs", [])
46
44
  servers: List[Dict[str, Any]] = config.get("servers", [])
47
45
 
48
46
  abort_event = asyncio.Event()
47
+ exit_event = asyncio.Event()
49
48
  first_sigint = True
50
49
 
51
50
  loop = asyncio.get_running_loop()
@@ -60,8 +59,7 @@ async def run_agent(
60
59
  return
61
60
 
62
61
  print("\n[red]Exiting...[/red]", flush=True)
63
-
64
- os._exit(130)
62
+ exit_event.set()
65
63
 
66
64
  try:
67
65
  sigint_registered_in_loop = False
@@ -71,6 +69,61 @@ async def run_agent(
71
69
  except (AttributeError, NotImplementedError):
72
70
  # Windows (or any loop that doesn't support it) : fall back to sync
73
71
  signal.signal(signal.SIGINT, lambda *_: _sigint_handler())
72
+
73
+ # Handle inputs (i.e. env variables injection)
74
+ if len(inputs) > 0:
75
+ print(
76
+ "[bold blue]Some initial inputs are required by the agent. "
77
+ "Please provide a value or leave empty to load from env.[/bold blue]"
78
+ )
79
+ for input_item in inputs:
80
+ input_id = input_item["id"]
81
+ description = input_item["description"]
82
+ env_special_value = "${input:" + input_id + "}" # Special value to indicate env variable injection
83
+
84
+ # Check env variables that will use this input
85
+ input_vars = list(
86
+ {
87
+ key
88
+ for server in servers
89
+ for key, value in server.get("config", {}).get("env", {}).items()
90
+ if value == env_special_value
91
+ }
92
+ )
93
+
94
+ if not input_vars:
95
+ print(f"[yellow]Input {input_id} defined in config but not used by any server.[/yellow]")
96
+ continue
97
+
98
+ # Prompt user for input
99
+ print(
100
+ f"[blue] • {input_id}[/blue]: {description}. (default: load from {', '.join(input_vars)}).",
101
+ end=" ",
102
+ )
103
+ user_input = (await _async_prompt(exit_event=exit_event)).strip()
104
+ if exit_event.is_set():
105
+ return
106
+
107
+ # Inject user input (or env variable) into servers' env
108
+ for server in servers:
109
+ env = server.get("config", {}).get("env", {})
110
+ for key, value in env.items():
111
+ if value == env_special_value:
112
+ if user_input:
113
+ env[key] = user_input
114
+ else:
115
+ value_from_env = os.getenv(key, "")
116
+ env[key] = value_from_env
117
+ if value_from_env:
118
+ print(f"[green]Value successfully loaded from '{key}'[/green]")
119
+ else:
120
+ print(
121
+ f"[yellow]No value found for '{key}' in environment variables. Continuing.[/yellow]"
122
+ )
123
+
124
+ print()
125
+
126
+ # Main agent loop
74
127
  async with Agent(
75
128
  provider=config.get("provider"),
76
129
  model=config.get("model"),
@@ -86,8 +139,12 @@ async def run_agent(
86
139
  while True:
87
140
  abort_event.clear()
88
141
 
142
+ # Check if we should exit
143
+ if exit_event.is_set():
144
+ return
145
+
89
146
  try:
90
- user_input = await _ainput()
147
+ user_input = await _async_prompt(exit_event=exit_event)
91
148
  first_sigint = True
92
149
  except EOFError:
93
150
  print("\n[red]EOF received, exiting.[/red]", flush=True)
@@ -103,6 +160,8 @@ async def run_agent(
103
160
  async for chunk in agent.run(user_input, abort_event=abort_event):
104
161
  if abort_event.is_set() and not first_sigint:
105
162
  break
163
+ if exit_event.is_set():
164
+ return
106
165
 
107
166
  if hasattr(chunk, "choices"):
108
167
  delta = chunk.choices[0].delta
@@ -109,6 +109,11 @@ class MCPClient:
109
109
  await self.client.__aexit__(exc_type, exc_val, exc_tb)
110
110
  await self.cleanup()
111
111
 
112
+ async def cleanup(self):
113
+ """Clean up resources"""
114
+ await self.client.close()
115
+ await self.exit_stack.aclose()
116
+
112
117
  @overload
113
118
  async def add_mcp_server(self, type: Literal["stdio"], **params: Unpack[StdioServerParameters_T]): ...
114
119
 
@@ -267,9 +272,6 @@ class MCPClient:
267
272
 
268
273
  # Read from stream
269
274
  async for chunk in response:
270
- # Yield each chunk to caller
271
- yield chunk
272
-
273
275
  num_of_chunks += 1
274
276
  delta = chunk.choices[0].delta if chunk.choices and len(chunk.choices) > 0 else None
275
277
  if not delta:
@@ -286,17 +288,22 @@ class MCPClient:
286
288
  for tool_call in delta.tool_calls:
287
289
  # Aggregate chunks into tool calls
288
290
  if tool_call.index not in final_tool_calls:
289
- if tool_call.function.arguments is None: # Corner case (depends on provider)
291
+ if (
292
+ tool_call.function.arguments is None or tool_call.function.arguments == "{}"
293
+ ): # Corner case (depends on provider)
290
294
  tool_call.function.arguments = ""
291
295
  final_tool_calls[tool_call.index] = tool_call
292
296
 
293
- if tool_call.function.arguments:
297
+ elif tool_call.function.arguments:
294
298
  final_tool_calls[tool_call.index].function.arguments += tool_call.function.arguments
295
299
 
296
300
  # Optionally exit early if no tools in first chunks
297
301
  if exit_if_first_chunk_no_tool and num_of_chunks <= 2 and len(final_tool_calls) == 0:
298
302
  return
299
303
 
304
+ # Yield each chunk to caller
305
+ yield chunk
306
+
300
307
  if message["content"]:
301
308
  messages.append(message)
302
309
 
@@ -327,7 +334,3 @@ class MCPClient:
327
334
  tool_message_as_obj = ChatCompletionInputMessage.parse_obj_as_instance(tool_message)
328
335
  messages.append(tool_message_as_obj)
329
336
  yield tool_message_as_obj
330
-
331
- async def cleanup(self):
332
- """Clean up resources"""
333
- await self.exit_stack.aclose()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: huggingface-hub
3
- Version: 0.32.2
3
+ Version: 0.32.4
4
4
  Summary: Client library to download and publish models, datasets and other repos on the huggingface.co hub
5
5
  Home-page: https://github.com/huggingface/huggingface_hub
6
6
  Author: Hugging Face, Inc.
@@ -1,4 +1,4 @@
1
- huggingface_hub/__init__.py,sha256=j0CPc82aFQA-FV3ew00Ukp6u4v-Lz3cBrH-K33Z50yE,50644
1
+ huggingface_hub/__init__.py,sha256=R8k-HmafEq-cYxRdFYM8fxWIj-0IjyKvMTb5R9-9QVA,50644
2
2
  huggingface_hub/_commit_api.py,sha256=ZbmuIhFdF8B3F_cvGtxorka7MmIQOk8oBkCtYltnCvI,39456
3
3
  huggingface_hub/_commit_scheduler.py,sha256=tfIoO1xWHjTJ6qy6VS6HIoymDycFPg0d6pBSZprrU2U,14679
4
4
  huggingface_hub/_inference_endpoints.py,sha256=qXR0utAYRaEWTI8EXzAsDpVDcYpp8bJPEBbcOxRS52E,17413
@@ -43,10 +43,10 @@ huggingface_hub/commands/upload_large_folder.py,sha256=P-EO44JWVl39Ax4b0E0Z873d0
43
43
  huggingface_hub/commands/user.py,sha256=_4rjCrP84KqtqCMn-r3YWLuGLrnklOWTdJFVTNFMLuU,7096
44
44
  huggingface_hub/commands/version.py,sha256=vfCJn7GO1m-DtDmbdsty8_RTVtnZ7lX6MJsx0Bf4e-s,1266
45
45
  huggingface_hub/inference/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
- huggingface_hub/inference/_client.py,sha256=o0R0Nkz11vhCjBWqDvMbGXU_MqZS1RHQOYr7QZDgML8,161570
46
+ huggingface_hub/inference/_client.py,sha256=9XhzTsC-87iGfRLW0grvGp53f4tE2QvvNCmOsrd1vJU,161538
47
47
  huggingface_hub/inference/_common.py,sha256=iwCkq2fWE1MVoPTeeXN7UN5FZi7g5fZ3K8PHSOCi5dU,14591
48
48
  huggingface_hub/inference/_generated/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
- huggingface_hub/inference/_generated/_async_client.py,sha256=CjGkQrFu4LX9JA056rEXmdokFmO7iZk98ND1D9ib1hg,167730
49
+ huggingface_hub/inference/_generated/_async_client.py,sha256=1rFYv_FcjaDW4F7L-QIYgkbWeFKgW6x3UdcZp1HYhZk,167698
50
50
  huggingface_hub/inference/_generated/types/__init__.py,sha256=qI8Eu9WcBcKhVkLli6YniGHpfiJ9MLqtzmwXX35E7bA,6443
51
51
  huggingface_hub/inference/_generated/types/audio_classification.py,sha256=Jg3mzfGhCSH6CfvVvgJSiFpkz6v4nNA0G4LJXacEgNc,1573
52
52
  huggingface_hub/inference/_generated/types/audio_to_audio.py,sha256=2Ep4WkePL7oJwcp5nRJqApwviumGHbft9HhXE9XLHj4,891
@@ -81,10 +81,11 @@ huggingface_hub/inference/_generated/types/zero_shot_classification.py,sha256=BA
81
81
  huggingface_hub/inference/_generated/types/zero_shot_image_classification.py,sha256=8J9n6VqFARkWvPfAZNWEG70AlrMGldU95EGQQwn06zI,1487
82
82
  huggingface_hub/inference/_generated/types/zero_shot_object_detection.py,sha256=GUd81LIV7oEbRWayDlAVgyLmY596r1M3AW0jXDp1yTA,1630
83
83
  huggingface_hub/inference/_mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
+ huggingface_hub/inference/_mcp/_cli_hacks.py,sha256=cMZirVFe4N0EM9Nzzs9aEmzUBUEBYR4oYZpByTWlZCM,3182
84
85
  huggingface_hub/inference/_mcp/agent.py,sha256=azX9_lsFjNlgsEvRYdKgsmOmpNReWIcbuMeIVWc852k,4264
85
- huggingface_hub/inference/_mcp/cli.py,sha256=q7WUlc7K9cHffKnS8uVydqM7IGxDz2gnwoNKkiRF5GU,5873
86
+ huggingface_hub/inference/_mcp/cli.py,sha256=6WP2bqdfSj80IYdwnzwO4ilEb7PgghgctD4iLV4ydqg,8588
86
87
  huggingface_hub/inference/_mcp/constants.py,sha256=tE_V6qcvsmvVoJa4eg04jhoTR2Cx1cNHieY2ENrm1_M,2511
87
- huggingface_hub/inference/_mcp/mcp_client.py,sha256=HUDkEURrWeQ9AY7W4_H4jca0BM12rKPbjdP4vNaXiwc,13998
88
+ huggingface_hub/inference/_mcp/mcp_client.py,sha256=jLIw_fkIPCGYbKI-T2C5UkYmCECJRGNGGw7gnVmXQ_s,14130
88
89
  huggingface_hub/inference/_mcp/utils.py,sha256=K7rr4FxCh9OYWwYNlnvQraNLy9y3z-5yVMBIaoCQMjA,4052
89
90
  huggingface_hub/inference/_providers/__init__.py,sha256=IrLTMERrbRuPiVdBQEMK9TMvXrsGId4-u2ucMkG-vTU,7671
90
91
  huggingface_hub/inference/_providers/_common.py,sha256=Octgz-PbHw62iW3Oa8rF7rxvBJR0ZmL4ouv3NoX-weE,10131
@@ -137,9 +138,9 @@ huggingface_hub/utils/insecure_hashlib.py,sha256=iAaepavFZ5Dhfa5n8KozRfQprKmvcjS
137
138
  huggingface_hub/utils/logging.py,sha256=0A8fF1yh3L9Ka_bCDX2ml4U5Ht0tY8Dr3JcbRvWFuwo,4909
138
139
  huggingface_hub/utils/sha.py,sha256=OFnNGCba0sNcT2gUwaVCJnldxlltrHHe0DS_PCpV3C4,2134
139
140
  huggingface_hub/utils/tqdm.py,sha256=xAKcyfnNHsZ7L09WuEM5Ew5-MDhiahLACbbN2zMmcLs,10671
140
- huggingface_hub-0.32.2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
141
- huggingface_hub-0.32.2.dist-info/METADATA,sha256=w2Df9xLc5FhDmwO11yIxMDiHc-i7aqLvXFRzRvzgI_E,14777
142
- huggingface_hub-0.32.2.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
143
- huggingface_hub-0.32.2.dist-info/entry_points.txt,sha256=uelw0-fu0kd-CxIuOsR1bsjLIFnAaMQ6AIqluJYDhQw,184
144
- huggingface_hub-0.32.2.dist-info/top_level.txt,sha256=8KzlQJAY4miUvjAssOAJodqKOw3harNzuiwGQ9qLSSk,16
145
- huggingface_hub-0.32.2.dist-info/RECORD,,
141
+ huggingface_hub-0.32.4.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
142
+ huggingface_hub-0.32.4.dist-info/METADATA,sha256=fwKn_Cxqvf8nVJPKBrc4TjfvAdCFZECGPjEBsBJkrhM,14777
143
+ huggingface_hub-0.32.4.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
144
+ huggingface_hub-0.32.4.dist-info/entry_points.txt,sha256=uelw0-fu0kd-CxIuOsR1bsjLIFnAaMQ6AIqluJYDhQw,184
145
+ huggingface_hub-0.32.4.dist-info/top_level.txt,sha256=8KzlQJAY4miUvjAssOAJodqKOw3harNzuiwGQ9qLSSk,16
146
+ huggingface_hub-0.32.4.dist-info/RECORD,,