jkorstad commited on
Commit
b593116
·
verified ·
1 Parent(s): 487e8eb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +46 -73
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 # Changed from "optional" to "nullable"
73
  }
74
  }
75
- output_type = "string" # Optional: define output type
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
- # top_k will be provided by the Tool base class mechanism.
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 can try creating a tool in the code like this: "
108
- "my_new_tool = Tool.from_space(repo_id='SPACE_ID_HERE', name='custom_tool_name'). "
109
- "Then you can call it: result = my_new_tool(argument_name=value). "
110
- "The arguments depend on the specific Space. If Tool.from_space fails or the tool doesn't work, "
111
- "the Space might not have a compatible public API or may require a specific api_name.")
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 (which will follow these instructions). Identify the core task and any specific requirements or inputs.
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, you MUST provide its arguments as a dictionary. For example: `huggingface_space_searcher(arguments={"query": "your search term", "top_k": 3})`. The `query` is mandatory. `top_k` is optional (defaults to 3 if not provided in the dictionary or if the key is absent).
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. Attempt to create a tool from it using `Tool.from_space(repo_id='SELECTED_SPACE_ID', name='a_unique_tool_name')`. You might need to give it a unique name. If `Tool.from_space` fails, the Space might not be compatible, or you could try another one from the search results.
170
- 5. **Execute the Tool:** Call the tool (either predefined or dynamically created) with the necessary arguments.
 
 
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
- * **Chaining Tools:** If the task requires multiple steps, chain the tools together.
173
  6. **Output Management:**
174
- * If a tool generates a file, save it to the current working directory using a unique filename (e.g., `output_filename = os.path.join(os.getcwd(), f"{uuid.uuid4()}.png")`).
175
- * **Return the RESULT:** Your final response should be either a string text answer or the string path to the generated output file.
176
- 7. **Clarity and Error Handling:** If you encounter issues, explain the problem.
177
 
178
- Example of dynamically using a Space after searching:
179
  ```python
180
- # search_results = huggingface_space_searcher(arguments={"query": "text to image cat", "top_k": 1}) # Note the arguments dictionary
181
- # print(search_results)
 
 
 
 
 
182
  # try:
183
- # cat_image_tool = Tool.from_space(repo_id="user/cat-generator", name="cat_generator_tool")
184
- # image_path = cat_image_tool(prompt="A fluffy siamese cat") # Arguments depend on the Space
185
- # return image_path
 
 
186
  # except Exception as e:
187
- # return f"Failed to use the cat generator Space: {e}"
 
 
 
 
 
 
 
 
 
 
 
188
  ```
189
  Always ensure your generated Python code is complete and directly callable.
190
- You have access to `os`, `uuid`, `PIL.Image`.
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)