Spaces:
Sleeping
Sleeping
Implement proper Hugging Face OAuth authentication
Browse files- Add gr.LoginButton to the sidebar for OAuth login
- Use gr.OAuthToken type hints for automatic token injection
- Update create_agent() to accept oauth_token parameter
- Create interact_with_agent_oauth() wrapper as a generator
- Update all event handlers to use OAuth-aware methods
- Priority: OAuth token > HF_TOKEN env var > CLI login
This ensures users can properly authenticate via OAuth on HF Spaces
and the agent receives the correct authentication token for API calls.
Fixes: "You must provide an api_key to work with nscale API" error
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
app.py
CHANGED
|
@@ -100,8 +100,13 @@ def get_tavily_search_mcp(api_key):
|
|
| 100 |
}
|
| 101 |
|
| 102 |
|
| 103 |
-
def create_agent(tavily_api_key=None):
|
| 104 |
-
"""Create an agent with the specified Tavily API key
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
# Use provided API key or fall back to environment variable
|
| 106 |
api_key = tavily_api_key or TAVILY_API_KEY
|
| 107 |
|
|
@@ -119,8 +124,14 @@ def create_agent(tavily_api_key=None):
|
|
| 119 |
mcp_client = MCPClient(mcp_servers, structured_output=False)
|
| 120 |
|
| 121 |
# Create model with user-specified parameters
|
| 122 |
-
#
|
| 123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
model = InferenceClientModel(
|
| 125 |
max_tokens=2096,
|
| 126 |
temperature=0.5,
|
|
|
|
| 100 |
}
|
| 101 |
|
| 102 |
|
| 103 |
+
def create_agent(tavily_api_key=None, oauth_token=None):
|
| 104 |
+
"""Create an agent with the specified Tavily API key and OAuth token.
|
| 105 |
+
|
| 106 |
+
Args:
|
| 107 |
+
tavily_api_key: Optional Tavily API key for web search
|
| 108 |
+
oauth_token: OAuth token from Gradio (gr.OAuthToken | None)
|
| 109 |
+
"""
|
| 110 |
# Use provided API key or fall back to environment variable
|
| 111 |
api_key = tavily_api_key or TAVILY_API_KEY
|
| 112 |
|
|
|
|
| 124 |
mcp_client = MCPClient(mcp_servers, structured_output=False)
|
| 125 |
|
| 126 |
# Create model with user-specified parameters
|
| 127 |
+
# Priority: OAuth token from Gradio > HF_TOKEN env var > get_token() from CLI login
|
| 128 |
+
if oauth_token is not None:
|
| 129 |
+
# On HF Spaces, use the OAuth token provided by Gradio
|
| 130 |
+
hf_token = oauth_token.token if hasattr(oauth_token, 'token') else oauth_token
|
| 131 |
+
else:
|
| 132 |
+
# Fallback to environment variable or CLI login
|
| 133 |
+
hf_token = os.getenv("HF_TOKEN") or get_token()
|
| 134 |
+
|
| 135 |
model = InferenceClientModel(
|
| 136 |
max_tokens=2096,
|
| 137 |
temperature=0.5,
|
my_ui.py
CHANGED
|
@@ -22,20 +22,31 @@ class CustomGradioUI(GradioUI):
|
|
| 22 |
self.allowed_file_types = allowed_file_types or [".pdf", ".docx", ".txt"]
|
| 23 |
self.examples = examples or []
|
| 24 |
|
| 25 |
-
def update_api_key(self, api_key, current_key):
|
| 26 |
"""Update the agent with a new API key."""
|
| 27 |
if api_key and api_key != current_key:
|
| 28 |
# Recreate the agent with the new API key
|
| 29 |
-
self.agent = self.agent_factory(tavily_api_key=api_key)
|
| 30 |
return api_key, gr.Markdown("✓ API key updated successfully", visible=True)
|
| 31 |
elif not api_key and current_key:
|
| 32 |
# Reset to default (env var)
|
| 33 |
-
self.agent = self.agent_factory()
|
| 34 |
return "", gr.Markdown(
|
| 35 |
"API key cleared, using environment variable if set", visible=True
|
| 36 |
)
|
| 37 |
return current_key, gr.Markdown("", visible=False)
|
| 38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
def create_app(self):
|
| 40 |
"""Override create_app to use custom allowed_file_types."""
|
| 41 |
|
|
@@ -57,6 +68,8 @@ class CustomGradioUI(GradioUI):
|
|
| 57 |
else ""
|
| 58 |
)
|
| 59 |
)
|
|
|
|
|
|
|
| 60 |
|
| 61 |
with gr.Group():
|
| 62 |
gr.Markdown("**Your request**", container=True)
|
|
@@ -144,7 +157,9 @@ class CustomGradioUI(GradioUI):
|
|
| 144 |
[text_input, file_uploads_log],
|
| 145 |
[stored_messages, text_input, submit_btn],
|
| 146 |
).then(
|
| 147 |
-
self.
|
|
|
|
|
|
|
| 148 |
).then(
|
| 149 |
lambda: (
|
| 150 |
gr.Textbox(
|
|
@@ -162,7 +177,9 @@ class CustomGradioUI(GradioUI):
|
|
| 162 |
[text_input, file_uploads_log],
|
| 163 |
[stored_messages, text_input, submit_btn],
|
| 164 |
).then(
|
| 165 |
-
self.
|
|
|
|
|
|
|
| 166 |
).then(
|
| 167 |
lambda: (
|
| 168 |
gr.Textbox(
|
|
|
|
| 22 |
self.allowed_file_types = allowed_file_types or [".pdf", ".docx", ".txt"]
|
| 23 |
self.examples = examples or []
|
| 24 |
|
| 25 |
+
def update_api_key(self, api_key, current_key, oauth_token: gr.OAuthToken | None = None):
|
| 26 |
"""Update the agent with a new API key."""
|
| 27 |
if api_key and api_key != current_key:
|
| 28 |
# Recreate the agent with the new API key
|
| 29 |
+
self.agent = self.agent_factory(tavily_api_key=api_key, oauth_token=oauth_token)
|
| 30 |
return api_key, gr.Markdown("✓ API key updated successfully", visible=True)
|
| 31 |
elif not api_key and current_key:
|
| 32 |
# Reset to default (env var)
|
| 33 |
+
self.agent = self.agent_factory(oauth_token=oauth_token)
|
| 34 |
return "", gr.Markdown(
|
| 35 |
"API key cleared, using environment variable if set", visible=True
|
| 36 |
)
|
| 37 |
return current_key, gr.Markdown("", visible=False)
|
| 38 |
|
| 39 |
+
def interact_with_agent_oauth(self, stored_messages, chatbot, session_state, oauth_token: gr.OAuthToken | None = None):
|
| 40 |
+
"""Wrapper for interact_with_agent that recreates agent with OAuth token."""
|
| 41 |
+
# Recreate agent with the OAuth token before interaction
|
| 42 |
+
if oauth_token is not None:
|
| 43 |
+
# Get current API key if any
|
| 44 |
+
current_api_key = session_state.get("tavily_api_key", None)
|
| 45 |
+
self.agent = self.agent_factory(tavily_api_key=current_api_key, oauth_token=oauth_token)
|
| 46 |
+
|
| 47 |
+
# Call the parent's interact_with_agent method and yield all results
|
| 48 |
+
yield from self.interact_with_agent(stored_messages, chatbot, session_state)
|
| 49 |
+
|
| 50 |
def create_app(self):
|
| 51 |
"""Override create_app to use custom allowed_file_types."""
|
| 52 |
|
|
|
|
| 68 |
else ""
|
| 69 |
)
|
| 70 |
)
|
| 71 |
+
# Add OAuth Login Button
|
| 72 |
+
gr.LoginButton()
|
| 73 |
|
| 74 |
with gr.Group():
|
| 75 |
gr.Markdown("**Your request**", container=True)
|
|
|
|
| 157 |
[text_input, file_uploads_log],
|
| 158 |
[stored_messages, text_input, submit_btn],
|
| 159 |
).then(
|
| 160 |
+
self.interact_with_agent_oauth,
|
| 161 |
+
[stored_messages, chatbot, session_state],
|
| 162 |
+
[chatbot]
|
| 163 |
).then(
|
| 164 |
lambda: (
|
| 165 |
gr.Textbox(
|
|
|
|
| 177 |
[text_input, file_uploads_log],
|
| 178 |
[stored_messages, text_input, submit_btn],
|
| 179 |
).then(
|
| 180 |
+
self.interact_with_agent_oauth,
|
| 181 |
+
[stored_messages, chatbot, session_state],
|
| 182 |
+
[chatbot]
|
| 183 |
).then(
|
| 184 |
lambda: (
|
| 185 |
gr.Textbox(
|