Skip to content

Commit 1b84f09

Browse files
dmpantiuclaude
andcommitted
feat: add HuggingFace token field, persist keys in sessionStorage for session only
Keys are saved to sessionStorage (survives refresh, cleared on browser close). On reconnect/refresh, keys auto-restore and re-send via WebSocket so users don't have to re-enter them. HuggingFace token is wired end-to-end through the WebSocket handler and agent session. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 332cc7c commit 1b84f09

File tree

5 files changed

+65
-7
lines changed

5 files changed

+65
-7
lines changed

web/agent_wrapper.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def _initialize(self):
6161
# Resolve API keys: user-provided take priority over env vars
6262
openai_key = self._api_keys.get("openai_api_key") or os.environ.get("OPENAI_API_KEY")
6363
arraylake_key = self._api_keys.get("arraylake_api_key") or os.environ.get("ARRAYLAKE_API_KEY")
64+
hf_token = self._api_keys.get("hf_token") or os.environ.get("HF_TOKEN")
6465

6566
if not arraylake_key:
6667
logger.warning("ARRAYLAKE_API_KEY not found")
@@ -69,6 +70,9 @@ def _initialize(self):
6970
# server-configured keys with user-provided ones in multi-user scenarios)
7071
os.environ["ARRAYLAKE_API_KEY"] = arraylake_key
7172

73+
if hf_token and not os.environ.get("HF_TOKEN"):
74+
os.environ["HF_TOKEN"] = hf_token
75+
7276
if not openai_key:
7377
logger.error("OPENAI_API_KEY not found")
7478
return

web/routes/api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ async def keys_status():
6060
return {
6161
"openai": bool(os.environ.get("OPENAI_API_KEY")),
6262
"arraylake": bool(os.environ.get("ARRAYLAKE_API_KEY")),
63+
"hf": bool(os.environ.get("HF_TOKEN")),
6364
}
6465

6566

web/routes/websocket.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ async def websocket_chat(websocket: WebSocket):
6464
api_keys = {
6565
"openai_api_key": data.get("openai_api_key", ""),
6666
"arraylake_api_key": data.get("arraylake_api_key", ""),
67+
"hf_token": data.get("hf_token", ""),
6768
}
6869
session = create_session(connection_id, api_keys=api_keys)
6970
ready = session.is_ready()

web/static/js/chat.js

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class EurusChat {
2626
this.saveKeysBtn = document.getElementById('save-keys-btn');
2727
this.openaiKeyInput = document.getElementById('openai-key');
2828
this.arraylakeKeyInput = document.getElementById('arraylake-key');
29+
this.hfKeyInput = document.getElementById('hf-key');
2930

3031
marked.setOptions({
3132
highlight: (code, lang) => {
@@ -80,33 +81,77 @@ class EurusChat {
8081
this.saveKeysBtn.addEventListener('click', () => this.saveAndSendKeys());
8182

8283
// Allow Enter in key fields to submit
83-
[this.openaiKeyInput, this.arraylakeKeyInput].forEach(input => {
84+
[this.openaiKeyInput, this.arraylakeKeyInput, this.hfKeyInput].forEach(input => {
8485
input.addEventListener('keydown', (e) => {
8586
if (e.key === 'Enter') {
8687
e.preventDefault();
8788
this.saveAndSendKeys();
8889
}
8990
});
9091
});
92+
93+
// Restore keys from sessionStorage (survives refresh, cleared on browser close)
94+
this.restoreSessionKeys();
95+
}
96+
97+
restoreSessionKeys() {
98+
const saved = sessionStorage.getItem('eurus-keys');
99+
if (!saved) return;
100+
try {
101+
const keys = JSON.parse(saved);
102+
if (keys.openai_api_key) this.openaiKeyInput.value = keys.openai_api_key;
103+
if (keys.arraylake_api_key) this.arraylakeKeyInput.value = keys.arraylake_api_key;
104+
if (keys.hf_token) this.hfKeyInput.value = keys.hf_token;
105+
} catch (e) {
106+
sessionStorage.removeItem('eurus-keys');
107+
}
108+
}
109+
110+
autoSendSessionKeys() {
111+
// After WS connects, if we have session-stored keys and server has none, auto-send them
112+
if (this.serverKeysPresent.openai || this.keysConfigured) return;
113+
const saved = sessionStorage.getItem('eurus-keys');
114+
if (!saved) return;
115+
try {
116+
const keys = JSON.parse(saved);
117+
if (keys.openai_api_key && this.ws && this.ws.readyState === WebSocket.OPEN) {
118+
this.ws.send(JSON.stringify({
119+
type: 'configure_keys',
120+
openai_api_key: keys.openai_api_key,
121+
arraylake_api_key: keys.arraylake_api_key || '',
122+
hf_token: keys.hf_token || '',
123+
}));
124+
}
125+
} catch (e) {
126+
sessionStorage.removeItem('eurus-keys');
127+
}
91128
}
92129

93130
saveAndSendKeys() {
94131
const openaiKey = this.openaiKeyInput.value.trim();
95132
const arraylakeKey = this.arraylakeKeyInput.value.trim();
133+
const hfKey = this.hfKeyInput.value.trim();
96134

97135
if (!openaiKey) {
98136
this.openaiKeyInput.focus();
99137
return;
100138
}
101139

102-
// Send keys via WebSocket (never saved to storage)
140+
// Save to sessionStorage (cleared when browser closes, survives refresh)
141+
const keysPayload = {
142+
openai_api_key: openaiKey,
143+
arraylake_api_key: arraylakeKey,
144+
hf_token: hfKey,
145+
};
146+
sessionStorage.setItem('eurus-keys', JSON.stringify(keysPayload));
147+
148+
// Send keys via WebSocket
103149
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
104150
this.saveKeysBtn.disabled = true;
105151
this.saveKeysBtn.textContent = 'Connecting...';
106152
this.ws.send(JSON.stringify({
107153
type: 'configure_keys',
108-
openai_api_key: openaiKey,
109-
arraylake_api_key: arraylakeKey,
154+
...keysPayload,
110155
}));
111156
}
112157
}
@@ -154,6 +199,9 @@ class EurusChat {
154199

155200
if (this.serverKeysPresent.openai || this.keysConfigured) {
156201
this.sendBtn.disabled = false;
202+
} else {
203+
// Auto-send keys from sessionStorage on reconnect/refresh
204+
this.autoSendSessionKeys();
157205
}
158206
};
159207

web/templates/index.html

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
<span>API Keys Required</span>
1111
</div>
1212
<div class="api-keys-body">
13-
<p class="api-keys-note">Enter your API keys to use Eurus. Keys are stored in your browser only and never
14-
saved on the server.</p>
13+
<p class="api-keys-note">Enter your API keys to use Eurus. Keys are kept in your browser session only
14+
they are cleared when you close the browser.</p>
1515
<div class="api-key-field">
1616
<label for="openai-key">OpenAI API Key <span class="required">*</span></label>
1717
<input type="password" id="openai-key" placeholder="sk-..." autocomplete="off">
@@ -20,6 +20,10 @@
2020
<label for="arraylake-key">Arraylake API Key</label>
2121
<input type="password" id="arraylake-key" placeholder="ema_..." autocomplete="off">
2222
</div>
23+
<div class="api-key-field">
24+
<label for="hf-key">HuggingFace Token</label>
25+
<input type="password" id="hf-key" placeholder="hf_..." autocomplete="off">
26+
</div>
2327
<button id="save-keys-btn" class="save-keys-btn">Connect</button>
2428
</div>
2529
</div>
@@ -60,5 +64,5 @@ <h3>Cached Datasets</h3>
6064
{% endblock %}
6165

6266
{% block scripts %}
63-
<script src="/static/js/chat.js?v=20260218"></script>
67+
<script src="/static/js/chat.js?v=20260218b"></script>
6468
{% endblock %}

0 commit comments

Comments
 (0)