Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -56,11 +56,8 @@ for space_info in spaces:
|
|
| 56 |
|
| 57 |
# --- Refactored HuggingFaceSpaceSearcherTool ---
|
| 58 |
class HuggingFaceSpaceSearcherTool(Tool):
|
| 59 |
-
# Define attributes as class variables
|
| 60 |
name = "huggingface_space_searcher"
|
| 61 |
description = "Searches for Hugging Face Spaces that can perform a specific task. Input is a search query string (e.g., 'text to image', 'speech recognition'). Returns a list of Space IDs, their descriptions, and instructions on how to try using them."
|
| 62 |
-
|
| 63 |
-
# Define input schema as required by smolagents.Tool base class
|
| 64 |
inputs = {
|
| 65 |
"query": {
|
| 66 |
"type": "string",
|
|
@@ -69,27 +66,18 @@ class HuggingFaceSpaceSearcherTool(Tool):
|
|
| 69 |
"top_k": {
|
| 70 |
"type": "integer",
|
| 71 |
"description": "The number of top results to return (default is 3).",
|
| 72 |
-
"nullable": True
|
| 73 |
}
|
| 74 |
}
|
| 75 |
-
output_type = "string"
|
| 76 |
|
| 77 |
-
# The core logic goes into the forward method
|
| 78 |
-
# The Tool base class will likely call this with arguments unpacked from a dictionary matching the 'inputs' schema.
|
| 79 |
def forward(self, query: str, top_k: int = 3) -> str:
|
| 80 |
-
"""
|
| 81 |
-
Searches Hugging Face Spaces for a given query and returns the top_k results.
|
| 82 |
-
Provides repo_id, description, likes, and last modified date for each space found.
|
| 83 |
-
"""
|
| 84 |
try:
|
| 85 |
-
|
| 86 |
-
# If "nullable": True and it's not provided by the agent, it might be None.
|
| 87 |
-
actual_top_k = top_k if top_k is not None else 3 # Ensure top_k has a value
|
| 88 |
print(f"Searching spaces with query: {query}, top_k: {actual_top_k}")
|
| 89 |
spaces_found = list(list_spaces(search=query, full=True, limit=actual_top_k, sort="likes", direction=-1))
|
| 90 |
if not spaces_found:
|
| 91 |
return "No Spaces found for your query."
|
| 92 |
-
|
| 93 |
results = "Found the following Spaces (sorted by likes):\n"
|
| 94 |
for i, space_data in enumerate(spaces_found):
|
| 95 |
description = "No description provided."
|
|
@@ -97,58 +85,26 @@ class HuggingFaceSpaceSearcherTool(Tool):
|
|
| 97 |
description = space_data.cardData['description']
|
| 98 |
elif hasattr(space_data, 'title') and space_data.title:
|
| 99 |
description = space_data.title
|
| 100 |
-
|
| 101 |
results += (
|
| 102 |
f"{i+1}. ID: {space_data.id}\n"
|
| 103 |
f" Description: {description}\n"
|
| 104 |
f" Likes: {space_data.likes if hasattr(space_data, 'likes') else 'N/A'}\n"
|
| 105 |
f" Last Modified: {space_data.lastModified if hasattr(space_data, 'lastModified') else 'N/A'}\n\n"
|
| 106 |
)
|
| 107 |
-
results += ("\nTo use one of these, you
|
| 108 |
-
"
|
| 109 |
-
"Then
|
| 110 |
-
"The arguments depend on the specific Space. If Tool.from_space fails
|
| 111 |
-
"the Space might not have a compatible public API
|
| 112 |
return results
|
| 113 |
except Exception as e:
|
| 114 |
print(f"Error searching Spaces: {str(e)}")
|
| 115 |
return f"Error searching Spaces: {str(e)}"
|
| 116 |
|
| 117 |
-
# Instantiate the custom tool
|
| 118 |
space_search_tool = HuggingFaceSpaceSearcherTool()
|
| 119 |
-
# ---- Debug print for the refactored tool ----
|
| 120 |
-
try:
|
| 121 |
-
print(f"\nDEBUG: 'space_search_tool' (refactored class) immediately after creation.")
|
| 122 |
-
print(f"DEBUG: Name: {space_search_tool.name}")
|
| 123 |
-
print(f"DEBUG: Inputs: {space_search_tool.inputs}") # Check if inputs are set
|
| 124 |
-
print(f"DEBUG: Type: {type(space_search_tool)}")
|
| 125 |
-
# print(f"DEBUG: All attributes: {dir(space_search_tool)}\n") # Can be verbose
|
| 126 |
-
except AttributeError as e:
|
| 127 |
-
print(f"\nDEBUG: 'space_search_tool' (refactored class) immediately after creation.")
|
| 128 |
-
print(f"DEBUG: Attribute MISSING. Error: {e}")
|
| 129 |
-
print(f"DEBUG: Type: {type(space_search_tool)}")
|
| 130 |
-
# print(f"DEBUG: All attributes: {dir(space_search_tool)}\n")
|
| 131 |
-
# ---- END Debug print ----
|
| 132 |
tools.append(space_search_tool)
|
| 133 |
|
| 134 |
-
|
| 135 |
-
# --- Debugging: Inspect tools before CodeAgent initialization ---
|
| 136 |
-
print("\n--- Inspecting tools before CodeAgent initialization ---")
|
| 137 |
-
for i, t in enumerate(tools):
|
| 138 |
-
if t is None:
|
| 139 |
-
print(f"Tool at index {i} is None!")
|
| 140 |
-
continue
|
| 141 |
-
try:
|
| 142 |
-
tool_name = t.name
|
| 143 |
-
print(f"Tool {i}: Name='{tool_name}', Type={type(t)}, Inputs: {getattr(t, 'inputs', 'Not defined')}")
|
| 144 |
-
except AttributeError:
|
| 145 |
-
print(f"!!! CRITICAL: Tool at index {i} (Type={type(t)}) is missing 'name' attribute.")
|
| 146 |
-
except Exception as e:
|
| 147 |
-
print(f"!!! ERROR inspecting tool at index {i} (Type={type(t)}): {str(e)}")
|
| 148 |
-
print("-------------------------------------------------------\n")
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
# Initialize the model - Use InferenceClientModel
|
| 152 |
model = InferenceClientModel(model_id="Qwen/Qwen2.5-Coder-32B-Instruct")
|
| 153 |
|
| 154 |
# Create the agent
|
|
@@ -162,32 +118,52 @@ agent = CodeAgent(
|
|
| 162 |
AGENT_INSTRUCTIONS = """You are a highly capable AI assistant. Your primary goal is to accomplish tasks using a variety of tools, prioritizing Hugging Face Spaces.
|
| 163 |
|
| 164 |
Follow these steps:
|
| 165 |
-
1. **Understand the Request:** Carefully analyze the user's prompt
|
| 166 |
2. **Check Predefined Tools:** Review your list of available tools. If a predefined tool can directly address the request, use it.
|
| 167 |
-
* For the 'huggingface_space_searcher' tool,
|
| 168 |
3. **Search for Spaces (If Needed):** If no predefined tool is suitable, use the `huggingface_space_searcher` tool as described above.
|
| 169 |
-
4. **Select and Instantiate a Space Tool:** From the search results, choose the most promising Space.
|
| 170 |
-
|
|
|
|
|
|
|
| 171 |
* **File Inputs:** If the user uploads files, their paths will be available as global string variables: `input_image_path`, `input_audio_path`, `input_video_path`, `input_3d_model_path`, `input_file_path`. Before using these variables, check if they exist and are not None. Pass these file paths as arguments to tools that require them.
|
| 172 |
-
* **
|
| 173 |
6. **Output Management:**
|
| 174 |
-
* If a tool
|
| 175 |
-
*
|
| 176 |
-
7. **Clarity and Error Handling:** If you encounter issues, explain the problem.
|
| 177 |
|
| 178 |
-
Example of
|
| 179 |
```python
|
| 180 |
-
#
|
| 181 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
# try:
|
| 183 |
-
#
|
| 184 |
-
#
|
| 185 |
-
#
|
|
|
|
|
|
|
| 186 |
# except Exception as e:
|
| 187 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
```
|
| 189 |
Always ensure your generated Python code is complete and directly callable.
|
| 190 |
-
You have access to `os`, `
|
| 191 |
"""
|
| 192 |
|
| 193 |
# Gradio interface function
|
|
@@ -248,9 +224,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
| 248 |
input_image = gr.Image(label="Image Input", type="filepath", sources=["upload", "clipboard"], elem_id="input_image_upload")
|
| 249 |
input_audio = gr.Audio(label="Audio Input", type="filepath", sources=["upload", "microphone"], elem_id="input_audio_upload")
|
| 250 |
with gr.Row():
|
| 251 |
-
# Removed type="filepath" from gr.Video
|
| 252 |
input_video = gr.Video(label="Video Input", sources=["upload"], elem_id="input_video_upload")
|
| 253 |
-
# Removed type="filepath" from gr.Model3D
|
| 254 |
input_model3d = gr.Model3D(label="3D Model Input", elem_id="input_model3d_upload")
|
| 255 |
with gr.Row():
|
| 256 |
input_file = gr.File(label="Generic File Input", type="filepath", elem_id="input_file_upload")
|
|
@@ -262,7 +236,6 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
| 262 |
image_output = gr.Image(label="Image Output", interactive=False, visible=False, show_download_button=True, elem_id="output_image_display")
|
| 263 |
audio_output = gr.Audio(label="Audio Output", interactive=False, visible=False, show_download_button=True, elem_id="output_audio_display")
|
| 264 |
with gr.Row():
|
| 265 |
-
# Removed show_download_button=True from gr.Model3D
|
| 266 |
model3d_output = gr.Model3D(label="3D Model Output", interactive=False, visible=False, elem_id="output_model3d_display")
|
| 267 |
text_output = gr.Textbox(label="Text / Log Output", interactive=False, visible=True, lines=5, max_lines=20, elem_id="output_text_log")
|
| 268 |
with gr.Row():
|
|
@@ -287,4 +260,4 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
| 287 |
)
|
| 288 |
|
| 289 |
if __name__ == "__main__":
|
| 290 |
-
app.launch(debug=True)
|
|
|
|
| 56 |
|
| 57 |
# --- Refactored HuggingFaceSpaceSearcherTool ---
|
| 58 |
class HuggingFaceSpaceSearcherTool(Tool):
|
|
|
|
| 59 |
name = "huggingface_space_searcher"
|
| 60 |
description = "Searches for Hugging Face Spaces that can perform a specific task. Input is a search query string (e.g., 'text to image', 'speech recognition'). Returns a list of Space IDs, their descriptions, and instructions on how to try using them."
|
|
|
|
|
|
|
| 61 |
inputs = {
|
| 62 |
"query": {
|
| 63 |
"type": "string",
|
|
|
|
| 66 |
"top_k": {
|
| 67 |
"type": "integer",
|
| 68 |
"description": "The number of top results to return (default is 3).",
|
| 69 |
+
"nullable": True
|
| 70 |
}
|
| 71 |
}
|
| 72 |
+
output_type = "string"
|
| 73 |
|
|
|
|
|
|
|
| 74 |
def forward(self, query: str, top_k: int = 3) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
try:
|
| 76 |
+
actual_top_k = top_k if top_k is not None else 3
|
|
|
|
|
|
|
| 77 |
print(f"Searching spaces with query: {query}, top_k: {actual_top_k}")
|
| 78 |
spaces_found = list(list_spaces(search=query, full=True, limit=actual_top_k, sort="likes", direction=-1))
|
| 79 |
if not spaces_found:
|
| 80 |
return "No Spaces found for your query."
|
|
|
|
| 81 |
results = "Found the following Spaces (sorted by likes):\n"
|
| 82 |
for i, space_data in enumerate(spaces_found):
|
| 83 |
description = "No description provided."
|
|
|
|
| 85 |
description = space_data.cardData['description']
|
| 86 |
elif hasattr(space_data, 'title') and space_data.title:
|
| 87 |
description = space_data.title
|
|
|
|
| 88 |
results += (
|
| 89 |
f"{i+1}. ID: {space_data.id}\n"
|
| 90 |
f" Description: {description}\n"
|
| 91 |
f" Likes: {space_data.likes if hasattr(space_data, 'likes') else 'N/A'}\n"
|
| 92 |
f" Last Modified: {space_data.lastModified if hasattr(space_data, 'lastModified') else 'N/A'}\n\n"
|
| 93 |
)
|
| 94 |
+
results += ("\nTo use one of these, you should first try creating a tool using "
|
| 95 |
+
"`Tool.from_space(repo_id='SPACE_ID_HERE', name='custom_tool_name')`. "
|
| 96 |
+
"Then call that new tool: `result = custom_tool_name(argument_name=value)`. "
|
| 97 |
+
"The arguments depend on the specific Space. If `Tool.from_space` fails, "
|
| 98 |
+
"the Space might not have a compatible public API.")
|
| 99 |
return results
|
| 100 |
except Exception as e:
|
| 101 |
print(f"Error searching Spaces: {str(e)}")
|
| 102 |
return f"Error searching Spaces: {str(e)}"
|
| 103 |
|
|
|
|
| 104 |
space_search_tool = HuggingFaceSpaceSearcherTool()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
tools.append(space_search_tool)
|
| 106 |
|
| 107 |
+
# Initialize the model
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
model = InferenceClientModel(model_id="Qwen/Qwen2.5-Coder-32B-Instruct")
|
| 109 |
|
| 110 |
# Create the agent
|
|
|
|
| 118 |
AGENT_INSTRUCTIONS = """You are a highly capable AI assistant. Your primary goal is to accomplish tasks using a variety of tools, prioritizing Hugging Face Spaces.
|
| 119 |
|
| 120 |
Follow these steps:
|
| 121 |
+
1. **Understand the Request:** Carefully analyze the user's prompt. Identify the core task and any specific requirements or inputs.
|
| 122 |
2. **Check Predefined Tools:** Review your list of available tools. If a predefined tool can directly address the request, use it.
|
| 123 |
+
* For the 'huggingface_space_searcher' tool, call it with direct keyword arguments like: `huggingface_space_searcher(query="your search term", top_k=3)`. The `query` is mandatory. `top_k` is optional and defaults to 3 if not provided.
|
| 124 |
3. **Search for Spaces (If Needed):** If no predefined tool is suitable, use the `huggingface_space_searcher` tool as described above.
|
| 125 |
+
4. **Select and Instantiate a Space Tool (PRIORITY):** From the search results, choose the most promising Space. **Your primary method to use this Space is to attempt to create a tool from it using `Tool.from_space(repo_id='SELECTED_SPACE_ID', name='a_unique_tool_name')`.** You *must* give it a unique and descriptive name.
|
| 126 |
+
* If `Tool.from_space()` succeeds, you now have a new tool. Call this new tool with the appropriate arguments for that Space.
|
| 127 |
+
* If `Tool.from_space()` fails (e.g., raises an exception), the Space might not have a compatible public API for this method. In this case, you can *then* consider if direct `gradio_client.Client` usage is feasible, but this is a fallback and often more complex.
|
| 128 |
+
5. **Execute the Tool:** Call the tool (predefined, or dynamically created via `Tool.from_space()`) with the necessary arguments.
|
| 129 |
* **File Inputs:** If the user uploads files, their paths will be available as global string variables: `input_image_path`, `input_audio_path`, `input_video_path`, `input_3d_model_path`, `input_file_path`. Before using these variables, check if they exist and are not None. Pass these file paths as arguments to tools that require them.
|
| 130 |
+
* **Imports in Generated Code:** If your code block for execution uses modules like `os` or `uuid`, **you must include the import statements (e.g., `import os`, `import uuid`) within that specific code block.**
|
| 131 |
6. **Output Management:**
|
| 132 |
+
* **If a tool returns a filepath string (e.g., to an image, audio, or other file), your final answer for this step should usually be that direct filepath string.** Do NOT attempt to re-save the file using `os.path.join` or `image.save()` unless you are performing an explicit transformation on the file content that requires loading and then saving. The system is designed to handle these returned filepaths.
|
| 133 |
+
* If a tool returns text, return that text.
|
| 134 |
+
7. **Clarity and Error Handling:** If you encounter issues (e.g., a Space tool fails, required inputs are missing), clearly explain the problem in your response. If a Space doesn't work, try to explain why or suggest an alternative if possible.
|
| 135 |
|
| 136 |
+
Example of the PREFERRED way to use a discovered Space:
|
| 137 |
```python
|
| 138 |
+
# User prompt: "Find a space that can make an image of a cat and use it."
|
| 139 |
+
#
|
| 140 |
+
# Step 1: Search for the space
|
| 141 |
+
# search_results = huggingface_space_searcher(query="text to image cat", top_k=1)
|
| 142 |
+
# print(search_results) # Assume 'someuser/cat-image-generator' is found.
|
| 143 |
+
#
|
| 144 |
+
# Step 2: Try to create a tool from the discovered space
|
| 145 |
# try:
|
| 146 |
+
# cat_tool = Tool.from_space(repo_id="someuser/cat-image-generator", name="cat_image_generator_tool")
|
| 147 |
+
# # Now use the newly created tool. Arguments depend on the Space's API.
|
| 148 |
+
# # Let's assume it takes a 'prompt'.
|
| 149 |
+
# image_filepath = cat_tool(prompt="A fluffy siamese cat, cyberpunk style")
|
| 150 |
+
# return image_filepath # Return the filepath directly
|
| 151 |
# except Exception as e:
|
| 152 |
+
# print(f"Failed to create or use tool from Space 'someuser/cat-image-generator': {e}")
|
| 153 |
+
# # Optionally, try another space or a predefined tool if appropriate.
|
| 154 |
+
# # return "Could not use the discovered space. Trying a fallback..." (then try another step)
|
| 155 |
+
```
|
| 156 |
+
|
| 157 |
+
Example of using a predefined tool that returns a filepath:
|
| 158 |
+
```python
|
| 159 |
+
# User prompt: "Generate an image of a happy robot."
|
| 160 |
+
# (Assuming 'image_generator_flux_schnell' is a predefined tool)
|
| 161 |
+
#
|
| 162 |
+
# image_filepath = image_generator_flux_schnell(prompt="A happy robot coding on a laptop, cyberpunk style")
|
| 163 |
+
# return image_filepath # Return the filepath string directly.
|
| 164 |
```
|
| 165 |
Always ensure your generated Python code is complete and directly callable.
|
| 166 |
+
You have access to `PIL.Image` (as `Image`), `os`, `sys`, `numpy`, `huggingface_hub`, `gradio_client`, `uuid`. Remember to import them if you use them in a code block.
|
| 167 |
"""
|
| 168 |
|
| 169 |
# Gradio interface function
|
|
|
|
| 224 |
input_image = gr.Image(label="Image Input", type="filepath", sources=["upload", "clipboard"], elem_id="input_image_upload")
|
| 225 |
input_audio = gr.Audio(label="Audio Input", type="filepath", sources=["upload", "microphone"], elem_id="input_audio_upload")
|
| 226 |
with gr.Row():
|
|
|
|
| 227 |
input_video = gr.Video(label="Video Input", sources=["upload"], elem_id="input_video_upload")
|
|
|
|
| 228 |
input_model3d = gr.Model3D(label="3D Model Input", elem_id="input_model3d_upload")
|
| 229 |
with gr.Row():
|
| 230 |
input_file = gr.File(label="Generic File Input", type="filepath", elem_id="input_file_upload")
|
|
|
|
| 236 |
image_output = gr.Image(label="Image Output", interactive=False, visible=False, show_download_button=True, elem_id="output_image_display")
|
| 237 |
audio_output = gr.Audio(label="Audio Output", interactive=False, visible=False, show_download_button=True, elem_id="output_audio_display")
|
| 238 |
with gr.Row():
|
|
|
|
| 239 |
model3d_output = gr.Model3D(label="3D Model Output", interactive=False, visible=False, elem_id="output_model3d_display")
|
| 240 |
text_output = gr.Textbox(label="Text / Log Output", interactive=False, visible=True, lines=5, max_lines=20, elem_id="output_text_log")
|
| 241 |
with gr.Row():
|
|
|
|
| 260 |
)
|
| 261 |
|
| 262 |
if __name__ == "__main__":
|
| 263 |
+
app.launch(debug=True)
|