Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions app/L0/_all/mod/_core/admin/views/agent/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import { mergeConsecutiveChatMessages } from "/mod/_core/framework/js/chat-messa
import * as proxyUrl from "/mod/_core/framework/js/proxy-url.js";
import { getHuggingFaceManager } from "/mod/_core/huggingface/manager.js";

function createHeaders(apiKey) {
function createHeaders(apiKey, bearerToken) {
const headers = {
"Content-Type": "application/json"
};

if (apiKey) {
if (bearerToken) {
headers.Authorization = `Bearer ${bearerToken}`;
} else if (apiKey) {
headers.Authorization = `Bearer ${apiKey}`;
}

Expand Down Expand Up @@ -442,7 +444,10 @@ export const prepareAdminAgentApiRequest = globalThis.space.extend(

return {
apiEndpoint,
headers: createHeaders(String(effectiveSettings?.apiKey || "").trim()),
headers: createHeaders(
String(effectiveSettings?.apiKey || "").trim(),
String(effectiveSettings?.bearerToken || "").trim()
),
messages: Array.isArray(messages) ? messages : [],
method: "POST",
promptContext: normalizedPromptContext,
Expand All @@ -461,8 +466,8 @@ async function streamAdminAgentApiCompletion({ promptContext, settings, systemPr
throw new Error("Set an API endpoint before sending a message.");
}

if (!settings.apiKey.trim()) {
throw new Error("Set an API key before sending a message.");
if (!settings.apiKey.trim() && !(settings.bearerToken || "").trim()) {
throw new Error("Set an API key or authorization bearer token before sending a message.");
}

if (!settings.model.trim()) {
Expand Down
1 change: 1 addition & 0 deletions app/L0/_all/mod/_core/admin/views/agent/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const ADMIN_CHAT_LOCAL_PROVIDER = {
export const DEFAULT_ADMIN_CHAT_SETTINGS = {
apiEndpoint: "https://openrouter.ai/api/v1/chat/completions",
apiKey: "",
bearerToken: "",
huggingfaceDtype: "q4",
huggingfaceModel: "",
localProvider: ADMIN_CHAT_LOCAL_PROVIDER.HUGGINGFACE,
Expand Down
7 changes: 7 additions & 0 deletions app/L0/_all/mod/_core/admin/views/agent/panel.html
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,13 @@ <h2>Provider and model configuration</h2>
<span>API Key</span>
<input type="password" x-model="$store.adminAgent.settingsDraft.apiKey" />
</label>
<label class="field">
<span>Authorization Bearer Token</span>
<input type="password" x-model="$store.adminAgent.settingsDraft.bearerToken" />
<p class="field-note">
Optional. When set, sent as <code>Authorization: Bearer</code> header instead of the API key.
</p>
</label>
</div>
<div x-show="$store.adminAgent.isSettingsDraftUsingLocalProvider" x-cloak class="admin-agent-local-settings">
<div class="admin-agent-local-provider-panel">
Expand Down
16 changes: 15 additions & 1 deletion app/L0/_all/mod/_core/admin/views/agent/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,16 @@ async function normalizeStoredConfig(runtime, parsedConfig) {
runtime,
storedConfig.api_key || storedConfig.apiKey || config.DEFAULT_ADMIN_CHAT_SETTINGS.apiKey || ""
);
const storedBearerToken = await decodeStoredApiKey(
runtime,
storedConfig.bearer_token || storedConfig.bearerToken || config.DEFAULT_ADMIN_CHAT_SETTINGS.bearerToken || ""
);

return {
settings: {
apiEndpoint: String(storedConfig.api_endpoint || storedConfig.apiEndpoint || config.DEFAULT_ADMIN_CHAT_SETTINGS.apiEndpoint || "").trim(),
apiKey: storedApiKey.value,
bearerToken: storedBearerToken.value,
huggingfaceDtype: String(
storedConfig.huggingface_dtype || storedConfig.huggingfaceDtype || config.DEFAULT_ADMIN_CHAT_SETTINGS.huggingfaceDtype || ""
).trim(),
Expand All @@ -156,7 +161,9 @@ async function normalizeStoredConfig(runtime, parsedConfig) {
promptBudgetRatios: normalizeStoredPromptBudgetRatios(storedConfig),
provider,
storedApiKeyLocked: storedApiKey.locked,
storedApiKeyValue: storedApiKey.storedValue
storedApiKeyValue: storedApiKey.storedValue,
storedBearerTokenLocked: storedBearerToken.locked,
storedBearerTokenValue: storedBearerToken.storedValue
},
systemPrompt: String(
storedConfig.custom_system_prompt ||
Expand All @@ -173,6 +180,11 @@ async function buildStoredConfigPayload(runtime, { settings, systemPrompt }) {
const payload = {
api_endpoint: String(settings?.apiEndpoint || config.DEFAULT_ADMIN_CHAT_SETTINGS.apiEndpoint || "").trim(),
api_key: await encodeStoredApiKey(runtime, settings),
bearer_token: await encodeStoredApiKey(runtime, {
apiKey: settings?.bearerToken,
storedApiKeyValue: settings?.storedBearerTokenValue,
storedApiKeyLocked: settings?.storedBearerTokenLocked
}),
huggingface_dtype: String(settings?.huggingfaceDtype || config.DEFAULT_ADMIN_CHAT_SETTINGS.huggingfaceDtype || "").trim(),
huggingface_model: String(settings?.huggingfaceModel || config.DEFAULT_ADMIN_CHAT_SETTINGS.huggingfaceModel || "").trim(),
local_provider: config.normalizeAdminChatLocalProvider(settings?.localProvider),
Expand Down Expand Up @@ -220,6 +232,8 @@ export async function saveAdminChatConfig(nextConfig) {
if (nextConfig?.settings && typeof nextConfig.settings === "object") {
nextConfig.settings.storedApiKeyLocked = false;
nextConfig.settings.storedApiKeyValue = String(payload.api_key || "").trim();
nextConfig.settings.storedBearerTokenLocked = false;
nextConfig.settings.storedBearerTokenValue = String(payload.bearer_token || "").trim();
}
} catch (error) {
throw new Error(`Unable to save admin chat config: ${error.message}`);
Expand Down
5 changes: 4 additions & 1 deletion app/L0/_all/mod/_core/admin/views/agent/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -1877,6 +1877,7 @@ const model = {
this.settings = {
apiEndpoint: (this.settingsDraft.apiEndpoint || "").trim(),
apiKey: (this.settingsDraft.apiKey || "").trim(),
bearerToken: (this.settingsDraft.bearerToken || "").trim(),
huggingfaceDtype: (this.settingsDraft.huggingfaceDtype || "").trim(),
huggingfaceModel: normalizeHuggingFaceModelInput(this.settingsDraft.huggingfaceModel || ""),
localProvider,
Expand All @@ -1886,7 +1887,9 @@ const model = {
promptBudgetRatios: clonePromptBudgetRatios(this.settingsDraft.promptBudgetRatios),
provider,
storedApiKeyLocked: this.settings.storedApiKeyLocked === true,
storedApiKeyValue: String(this.settings.storedApiKeyValue || "")
storedApiKeyValue: String(this.settings.storedApiKeyValue || ""),
storedBearerTokenLocked: this.settings.storedBearerTokenLocked === true,
storedBearerTokenValue: String(this.settings.storedBearerTokenValue || "")
};

try {
Expand Down
15 changes: 10 additions & 5 deletions app/L0/_all/mod/_core/onscreen_agent/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,14 @@ function normalizeCompletionMessagesForLocal(messages) {
.filter(Boolean);
}

function createApiRequestHeaders(apiKey) {
function createApiRequestHeaders(apiKey, bearerToken) {
const headers = {
"Content-Type": "application/json"
};

if (apiKey) {
if (bearerToken) {
headers.Authorization = `Bearer ${bearerToken}`;
} else if (apiKey) {
headers.Authorization = `Bearer ${apiKey}`;
}

Expand Down Expand Up @@ -303,8 +305,8 @@ export class OnscreenAgentApiLlmClient extends OnscreenAgentLlmClient {
throw new Error("Set an API endpoint before sending a message.");
}

if (!settings.apiKey.trim()) {
throw new Error("Set an API key before sending a message.");
if (!settings.apiKey.trim() && !(settings.bearerToken || "").trim()) {
throw new Error("Set an API key or authorization bearer token before sending a message.");
}

if (!settings.model.trim()) {
Expand Down Expand Up @@ -421,7 +423,10 @@ export const prepareOnscreenAgentApiRequest = globalThis.space.extend(

return {
apiEndpoint,
headers: createApiRequestHeaders(String(effectiveSettings?.apiKey || "").trim()),
headers: createApiRequestHeaders(
String(effectiveSettings?.apiKey || "").trim(),
String(effectiveSettings?.bearerToken || "").trim()
),
messages: Array.isArray(effectivePreparedRequest?.messages) ? effectivePreparedRequest.messages : [],
method: "POST",
preparedRequest: effectivePreparedRequest,
Expand Down
1 change: 1 addition & 0 deletions app/L0/_all/mod/_core/onscreen_agent/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const ONSCREEN_AGENT_HIDDEN_EDGE = Object.freeze({
export const DEFAULT_ONSCREEN_AGENT_SETTINGS = {
apiEndpoint: "https://openrouter.ai/api/v1/chat/completions",
apiKey: "",
bearerToken: "",
huggingfaceDtype: "q4",
huggingfaceModel: "",
localProvider: ONSCREEN_AGENT_LOCAL_PROVIDER.HUGGINGFACE,
Expand Down
7 changes: 7 additions & 0 deletions app/L0/_all/mod/_core/onscreen_agent/panel.html
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,13 @@ <h2>Model, credentials, params, and instructions</h2>
<span>API Key</span>
<input type="password" x-model="$store.onscreenAgent.settingsDraft.apiKey" />
</label>
<label class="field">
<span>Authorization Bearer Token</span>
<input type="password" x-model="$store.onscreenAgent.settingsDraft.bearerToken" />
<p class="field-note">
Optional. When set, sent as <code>Authorization: Bearer</code> header instead of the API key.
</p>
</label>
</div>
<div x-show="$store.onscreenAgent.isSettingsDraftUsingLocalProvider" x-cloak class="onscreen-agent-local-settings">
<div class="onscreen-agent-local-provider-panel">
Expand Down
16 changes: 15 additions & 1 deletion app/L0/_all/mod/_core/onscreen_agent/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ async function normalizeStoredConfig(runtime, parsedConfig) {
runtime,
storedConfig.api_key || storedConfig.apiKey || config.DEFAULT_ONSCREEN_AGENT_SETTINGS.apiKey || ""
);
const storedBearerToken = await decodeStoredApiKey(
runtime,
storedConfig.bearer_token || storedConfig.bearerToken || config.DEFAULT_ONSCREEN_AGENT_SETTINGS.bearerToken || ""
);
const legacyDisplayMode =
storedConfig.collapsed === true
? DISPLAY_MODE_COMPACT
Expand All @@ -213,6 +217,7 @@ async function normalizeStoredConfig(runtime, parsedConfig) {
settings: {
apiEndpoint: String(storedConfig.api_endpoint || storedConfig.apiEndpoint || config.DEFAULT_ONSCREEN_AGENT_SETTINGS.apiEndpoint || "").trim(),
apiKey: storedApiKey.value,
bearerToken: storedBearerToken.value,
huggingfaceDtype: String(
storedConfig.huggingface_dtype ||
storedConfig.huggingfaceDtype ||
Expand All @@ -232,7 +237,9 @@ async function normalizeStoredConfig(runtime, parsedConfig) {
promptBudgetRatios: normalizeStoredPromptBudgetRatios(storedConfig),
provider,
storedApiKeyLocked: storedApiKey.locked,
storedApiKeyValue: storedApiKey.storedValue
storedApiKeyValue: storedApiKey.storedValue,
storedBearerTokenLocked: storedBearerToken.locked,
storedBearerTokenValue: storedBearerToken.storedValue
},
systemPrompt: String(
storedConfig.custom_system_prompt ||
Expand Down Expand Up @@ -278,6 +285,11 @@ async function buildStoredConfigPayload(runtime, { settings, systemPrompt }) {
const payload = {
api_endpoint: String(settings?.apiEndpoint || config.DEFAULT_ONSCREEN_AGENT_SETTINGS.apiEndpoint || "").trim(),
api_key: await encodeStoredApiKey(runtime, settings),
bearer_token: await encodeStoredApiKey(runtime, {
apiKey: settings?.bearerToken,
storedApiKeyValue: settings?.storedBearerTokenValue,
storedApiKeyLocked: settings?.storedBearerTokenLocked
}),
huggingface_dtype: String(settings?.huggingfaceDtype || config.DEFAULT_ONSCREEN_AGENT_SETTINGS.huggingfaceDtype || "").trim(),
huggingface_model: String(settings?.huggingfaceModel || config.DEFAULT_ONSCREEN_AGENT_SETTINGS.huggingfaceModel || "").trim(),
local_provider: config.normalizeOnscreenAgentLocalProvider(settings?.localProvider),
Expand Down Expand Up @@ -446,6 +458,8 @@ export async function saveOnscreenAgentConfig(nextConfig) {
if (nextConfig?.settings && typeof nextConfig.settings === "object") {
nextConfig.settings.storedApiKeyLocked = false;
nextConfig.settings.storedApiKeyValue = String(payload.api_key || "").trim();
nextConfig.settings.storedBearerTokenLocked = false;
nextConfig.settings.storedBearerTokenValue = String(payload.bearer_token || "").trim();
}
} catch (error) {
throw new Error(`Unable to save onscreen agent config: ${error.message}`);
Expand Down
5 changes: 4 additions & 1 deletion app/L0/_all/mod/_core/onscreen_agent/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -4604,6 +4604,7 @@ const model = {
this.settings = {
apiEndpoint: (this.settingsDraft.apiEndpoint || "").trim(),
apiKey: (this.settingsDraft.apiKey || "").trim(),
bearerToken: (this.settingsDraft.bearerToken || "").trim(),
huggingfaceDtype: (this.settingsDraft.huggingfaceDtype || "").trim(),
huggingfaceModel: normalizeHuggingFaceModelInput(this.settingsDraft.huggingfaceModel || ""),
localProvider,
Expand All @@ -4613,7 +4614,9 @@ const model = {
promptBudgetRatios: clonePromptBudgetRatios(this.settingsDraft.promptBudgetRatios),
provider,
storedApiKeyLocked: this.settings.storedApiKeyLocked === true,
storedApiKeyValue: String(this.settings.storedApiKeyValue || "")
storedApiKeyValue: String(this.settings.storedApiKeyValue || ""),
storedBearerTokenLocked: this.settings.storedBearerTokenLocked === true,
storedBearerTokenValue: String(this.settings.storedBearerTokenValue || "")
};
this.systemPrompt = draftPrompt;
this.systemPromptDraft = draftPrompt;
Expand Down