Skip to content
Merged
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
2 changes: 1 addition & 1 deletion application/single_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
EXECUTOR_TYPE = 'thread'
EXECUTOR_MAX_WORKERS = 30
SESSION_TYPE = 'filesystem'
VERSION = "0.250.109"
VERSION = "0.250.110"
IS_DEVELOPMENT = is_development_env_enabled()

SESSION_COOKIE_SAMESITE = os.getenv('SESSION_COOKIE_SAMESITE', 'Lax')
Expand Down
68 changes: 65 additions & 3 deletions application/single_app/functions_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@
ADMIN_SETTINGS_NESTED_SECRET_FIELDS = (
"web_search_agent.other_settings.azure_ai_foundry.client_secret",
)
PUBLIC_WORKSPACE_DISPLAY_NAME_DEFAULT = "Public Workspace"

Check warning on line 89 in application/single_app/functions_settings.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
PUBLIC_WORKSPACE_DISPLAY_NAME_PLURAL_DEFAULT = "Public Workspaces"

Check warning on line 90 in application/single_app/functions_settings.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
PUBLIC_WORKSPACE_DISPLAY_NAME_MAX_LENGTH = 32

Check warning on line 91 in application/single_app/functions_settings.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.


def is_admin_settings_redacted_secret(value):
Expand Down Expand Up @@ -118,6 +121,59 @@
return str(_get_nested_setting_value(existing_settings, field_name) or '').strip()


def normalize_public_workspace_display_name(value):

Check warning on line 124 in application/single_app/functions_settings.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
"""Return the end-user Public Workspace display name setting."""
display_name = " ".join(str(value or "").replace("\r", " ").replace("\n", " ").split())
return display_name[:PUBLIC_WORKSPACE_DISPLAY_NAME_MAX_LENGTH]

Check warning on line 127 in application/single_app/functions_settings.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.


def get_public_workspace_label_context(settings=None):

Check warning on line 130 in application/single_app/functions_settings.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
"""Return safe labels for end-user Public Workspace UI copy."""
source_settings = settings if isinstance(settings, dict) else {}
custom_display_name = normalize_public_workspace_display_name(

Check warning on line 133 in application/single_app/functions_settings.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
source_settings.get("public_workspace_display_name")

Check warning on line 134 in application/single_app/functions_settings.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
)
is_custom = bool(custom_display_name)
singular = custom_display_name or PUBLIC_WORKSPACE_DISPLAY_NAME_DEFAULT

Check warning on line 137 in application/single_app/functions_settings.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
plural = custom_display_name or PUBLIC_WORKSPACE_DISPLAY_NAME_PLURAL_DEFAULT

Check warning on line 138 in application/single_app/functions_settings.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
lower_singular = singular if is_custom else "public workspace"
lower_plural = plural if is_custom else "public workspaces"
return {
"singular": singular,
"plural": plural,
"lower_singular": lower_singular,
"lower_plural": lower_plural,
"short": singular if is_custom else "Public",
"is_custom": is_custom,
"max_length": PUBLIC_WORKSPACE_DISPLAY_NAME_MAX_LENGTH,
}


def normalize_public_workspace_display_settings(settings):
"""Normalize stored Public Workspace display-name settings in-place."""
if not isinstance(settings, dict):
return False

changed = False
if "public_workspace_labels" in settings:
settings.pop("public_workspace_labels", None)
changed = True

normalized_display_name = normalize_public_workspace_display_name(
settings.get("public_workspace_display_name")
)
changed = changed or settings.get("public_workspace_display_name", "") != normalized_display_name
settings["public_workspace_display_name"] = normalized_display_name
return changed


def attach_public_workspace_label_context(settings):
"""Attach derived end-user Public Workspace label values to a settings dict."""
if isinstance(settings, dict):
settings["public_workspace_labels"] = get_public_workspace_label_context(settings)
return settings


def normalize_document_access_index_required_settings(settings):
"""Force DAI operational settings that are required for the default read path."""
if not isinstance(settings, dict):
Expand Down Expand Up @@ -1024,6 +1080,7 @@
'require_member_of_create_group': False,
'require_owner_for_group_agent_management': False,
'enable_public_workspaces': False,
'public_workspace_display_name': '',
'require_member_of_create_public_workspace': False,
'enable_file_sharing': False,
'allow_personal_workspace_file_downloads': False,
Expand Down Expand Up @@ -1457,6 +1514,7 @@
promoted_popular_settings_updated = normalize_agents_page_promoted_popular_settings(merged)
document_access_index_settings_updated = normalize_document_access_index_required_settings(merged)
inbound_mcp_settings_updated = normalize_inbound_mcp_settings(merged)
public_workspace_display_settings_updated = normalize_public_workspace_display_settings(merged)

merged['enable_tabular_processing_plugin'] = is_tabular_processing_enabled(merged)

Expand All @@ -1469,6 +1527,7 @@
or promoted_popular_settings_updated
or document_access_index_settings_updated
or inbound_mcp_settings_updated
or public_workspace_display_settings_updated
):
cosmos_settings_container.upsert_item(merged)
_refresh_app_settings_cache_after_write(merged, context="merge_upsert")
Expand All @@ -1480,10 +1539,10 @@
},
level=logging.INFO
)
return _format_result(merged, settings_source)
return _format_result(attach_public_workspace_label_context(merged), settings_source)
else:
# If merged is unchanged, no new keys needed
return _format_result(merged, settings_source)
return _format_result(attach_public_workspace_label_context(merged), settings_source)

except CosmosResourceNotFoundError:
cosmos_settings_container.create_item(body=default_settings)
Expand All @@ -1496,7 +1555,7 @@
},
level=logging.WARNING
)
return _format_result(default_settings, "cosmos_default_created")
return _format_result(attach_public_workspace_label_context(default_settings), "cosmos_default_created")

except Exception as e:
log_event(
Expand All @@ -1520,6 +1579,7 @@
normalize_agents_page_promoted_popular_settings(settings_item)
normalize_document_access_index_required_settings(settings_item)
normalize_inbound_mcp_settings(settings_item)
normalize_public_workspace_display_settings(settings_item)
settings_item['enable_multi_model_endpoints'] = coerce_multi_model_endpoint_enablement(
existing_multi_endpoint_enabled,
settings_item.get('enable_multi_model_endpoints', False),
Expand Down Expand Up @@ -2606,6 +2666,8 @@
'enabled': False,
}

sanitized['public_workspace_labels'] = get_public_workspace_label_context(full_settings)

return sanitized

def sanitize_settings_for_logging(full_settings: dict) -> dict:
Expand Down
4 changes: 4 additions & 0 deletions application/single_app/route_frontend_admin_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,9 @@ def parse_admin_int(raw_value, fallback_value, field_name="unknown", hard_defaul

require_member_of_create_group = form_data.get('require_member_of_create_group') == 'on'
require_owner_for_group_agent_management = form_data.get('require_owner_for_group_agent_management') == 'on'
public_workspace_display_name = normalize_public_workspace_display_name(
form_data.get('public_workspace_display_name')
)
require_member_of_create_public_workspace = form_data.get('require_member_of_create_public_workspace') == 'on'
require_member_of_chat_file_upload_user = form_data.get('require_member_of_chat_file_upload_user') == 'on'
require_member_of_workflow_user = form_data.get('require_member_of_workflow_user') == 'on'
Expand Down Expand Up @@ -2434,6 +2437,7 @@ def is_valid_url(url):
# disable_group_creation is inverted: when checked (on), enable_group_creation = False
'enable_group_creation': form_data.get('disable_group_creation') != 'on',
'enable_public_workspaces': form_data.get('enable_public_workspaces') == 'on',
'public_workspace_display_name': public_workspace_display_name,
'enable_file_sharing': form_data.get('enable_file_sharing') == 'on',
'enable_chat_file_uploads': form_data.get('enable_chat_file_uploads') == 'on',
'enable_conversation_contents_drawer': form_data.get('enable_conversation_contents_drawer') == 'on',
Expand Down
6 changes: 5 additions & 1 deletion application/single_app/static/js/agent_modal_stepper.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { getModelSupportedLevels } from "./chat/chat-reasoning.js";

const ACTION_CAPABILITIES_KEY = 'action_capabilities';
const ASSIGNED_KNOWLEDGE_KEY = 'assigned_knowledge';
const publicWorkspaceLowerSingular = window.getPublicWorkspaceLabel ? window.getPublicWorkspaceLabel('lower_singular') : 'public workspace';
const publicWorkspaceLowerPlural = window.getPublicWorkspaceLabel ? window.getPublicWorkspaceLabel('lower_plural') : 'public workspaces';
const ASSIGNED_KNOWLEDGE_USER_ACTIONS = Object.freeze(['search', 'analyze', 'compare']);
const ASSIGNED_KNOWLEDGE_WEB_SOURCE_MODES = Object.freeze(['url_review', 'deep_research']);
const EMPTY_ASSIGNED_KNOWLEDGE = Object.freeze({
Expand Down Expand Up @@ -3094,7 +3096,9 @@ export class AgentModalStepper {
summaryItems.push(`${scopes.group_ids.length} group source${scopes.group_ids.length === 1 ? '' : 's'}`);
}
if (scopes.public_workspace_ids?.length) {
summaryItems.push(`${scopes.public_workspace_ids.length} public workspace${scopes.public_workspace_ids.length === 1 ? '' : 's'}`);
const publicWorkspaceCount = scopes.public_workspace_ids.length;
const publicWorkspaceLabel = publicWorkspaceCount === 1 ? publicWorkspaceLowerSingular : publicWorkspaceLowerPlural;
summaryItems.push(`${publicWorkspaceCount} ${publicWorkspaceLabel}`);
}
if (assignedKnowledge.document_ids?.length) {
summaryItems.push(`${assignedKnowledge.document_ids.length} specific document${assignedKnowledge.document_ids.length === 1 ? '' : 's'}`);
Expand Down
3 changes: 2 additions & 1 deletion application/single_app/static/js/chat/chat-documents.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const searchDocumentsBtn = document.getElementById("search-documents-btn");
const docSelectEl = document.getElementById("document-select"); // Hidden select element
const searchDocumentsContainer = document.getElementById("search-documents-container"); // Container for scope/doc/class
const searchDocumentsMobileClose = document.getElementById("search-documents-mobile-close");
const publicWorkspacePlural = window.getPublicWorkspaceLabel ? window.getPublicWorkspaceLabel("plural") : "Public Workspaces";

// Custom dropdown elements
const docDropdown = document.getElementById("document-dropdown");
Expand Down Expand Up @@ -1114,7 +1115,7 @@ function buildScopeDropdown() {
if (publicWorkspaces.length > 0) {
const pubHeader = document.createElement("div");
pubHeader.classList.add("dropdown-header", "small", "text-muted", "px-2", "pt-2", "pb-1");
pubHeader.textContent = "Public Workspaces";
pubHeader.textContent = publicWorkspacePlural;
scopeDropdownItems.appendChild(pubHeader);

publicWorkspaces.forEach(ws => {
Expand Down
8 changes: 5 additions & 3 deletions application/single_app/static/js/chat/chat-onload.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import { initializeReasoningToggle } from "./chat-reasoning.js";
import { initializeSpeechInput } from "./chat-speech-input.js";
import { initChatTutorial } from "./chat-tutorial.js";

const publicWorkspaceLowerSingular = window.getPublicWorkspaceLabel ? window.getPublicWorkspaceLabel("lower_singular") : "public workspace";


function clearFeatureActionParam() {
const url = new URL(window.location.href);
Expand Down Expand Up @@ -228,17 +230,17 @@ window.addEventListener('DOMContentLoaded', async () => {
// Trigger change to update UI
handleDocumentSelectChange();

showToast('Public workspace activated for chat', 'success');
showToast(`${publicWorkspaceLowerSingular} activated for chat`, 'success');
} else {
console.error('Failed to set active public workspace:', data.error || data.message);
showToast('Failed to activate public workspace', 'error');
showToast(`Failed to activate ${publicWorkspaceLowerSingular}`, 'error');
// Fall back to normal document handling
populateDocumentSelectScope();
}
})
.catch(error => {
console.error('Error setting active public workspace:', error);
showToast('Error activating public workspace', 'error');
showToast(`Error activating ${publicWorkspaceLowerSingular}`, 'error');
// Fall back to normal document handling
populateDocumentSelectScope();
});
Expand Down
3 changes: 2 additions & 1 deletion application/single_app/static/js/chat/chat-tutorial.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const STORAGE_KEY = "chatTutorialDismissed";
const EDGE_PADDING = 12;
const HIGHLIGHT_PADDING = 10;
const publicWorkspaceLowerPlural = window.getPublicWorkspaceLabel ? window.getPublicWorkspaceLabel("lower_plural") : "public workspaces";
let tutorialSteps = [];
let layerEl = null;
let highlightEl = null;
Expand Down Expand Up @@ -197,7 +198,7 @@ function buildSteps() {
id: "workspace-search",
selector: "#search-documents-btn",
title: "Workspace search",
body: "Search personal, group, or public workspaces to ground answers with approved documents.",
body: `Search personal, group, or ${publicWorkspaceLowerPlural} to ground answers with approved documents.`,
phase: "chat"
},
{
Expand Down
9 changes: 5 additions & 4 deletions application/single_app/static/js/form-voice-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
(function () {
const MAX_RECORDING_DURATION_MS = 90000;
const DEFAULT_TRANSCRIPTION_ENDPOINT = '/api/speech/transcribe-chat';
const publicWorkspaceLowerSingular = window.getPublicWorkspaceLabel ? window.getPublicWorkspaceLabel('lower_singular') : 'public workspace';
const fieldControls = new WeakMap();
const controls = [];
let activeControl = null;
Expand Down Expand Up @@ -509,10 +510,10 @@
['groupDescription', { label: 'Dictate group description' }],
['editGroupName', { label: 'Dictate group name', insertMode: 'replace' }],
['editGroupDescription', { label: 'Dictate group description' }],
['publicWorkspaceName', { label: 'Dictate public workspace name', insertMode: 'replace' }],
['publicWorkspaceDescription', { label: 'Dictate public workspace description' }],
['editWorkspaceName', { label: 'Dictate public workspace name', insertMode: 'replace' }],
['editWorkspaceDescription', { label: 'Dictate public workspace description' }],
['publicWorkspaceName', { label: `Dictate ${publicWorkspaceLowerSingular} name`, insertMode: 'replace' }],
['publicWorkspaceDescription', { label: `Dictate ${publicWorkspaceLowerSingular} description` }],
['editWorkspaceName', { label: `Dictate ${publicWorkspaceLowerSingular} name`, insertMode: 'replace' }],
['editWorkspaceDescription', { label: `Dictate ${publicWorkspaceLowerSingular} description` }],
['doc-title', { label: 'Dictate title', insertMode: 'replace' }],
['doc-abstract', { label: 'Dictate abstract' }],
['doc-keywords', { label: 'Dictate keywords', mode: 'comma-list' }],
Expand Down
3 changes: 2 additions & 1 deletion application/single_app/static/js/plugin_modal_stepper.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const SNOWFLAKE_AUTH_METHOD_OAUTH = 'oauth';
const TABLEAU_PLUGIN_TYPE = 'tableau';
const TABLEAU_AUTH_METHOD_PAT = 'personal_access_token';
const TABLEAU_AUTH_METHOD_USERNAME_PASSWORD = 'username_password';
const publicWorkspacePlural = window.getPublicWorkspaceLabel ? window.getPublicWorkspaceLabel('plural') : 'Public Workspaces';
const MCP_PLUGIN_TYPE = 'mcp';
const MCP_HEADER_NAME_PATTERN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
const MCP_MAX_CUSTOM_HEADER_COUNT = 20;
Expand Down Expand Up @@ -2284,7 +2285,7 @@ export class PluginModalStepper {
all: 'All Accessible Content',
personal: 'Personal Workspace',
group: 'Group Workspaces',
public: 'Public Workspaces'
public: publicWorkspacePlural
};

return scopeMap[scope] || scope || '-';
Expand Down
12 changes: 7 additions & 5 deletions application/single_app/static/js/profile/profile-tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

(function () {
const pageConfig = window.profilePageConfig || {};
const publicWorkspaceLowerSingular = window.getPublicWorkspaceLabel ? window.getPublicWorkspaceLabel('lower_singular') : 'public workspace';
const publicWorkspaceLowerPlural = window.getPublicWorkspaceLabel ? window.getPublicWorkspaceLabel('lower_plural') : 'public workspaces';
const feedbackState = {
currentPage: 1,
pageSize: 10,
Expand Down Expand Up @@ -371,8 +373,8 @@
},
publicWorkspaces: {
type: 'publicWorkspaces',
label: 'public workspaces',
itemLabel: 'public workspace',
label: publicWorkspaceLowerPlural,
itemLabel: publicWorkspaceLowerSingular,
state: publicWorkspaceState,
apiEndpoint: '/api/public_workspaces',
responseKey: 'workspaces',
Expand Down Expand Up @@ -409,9 +411,9 @@
discoverStatusId: 'profile-find-public-workspaces-status',
discoverTbodyId: 'profile-find-public-workspaces-tbody',
storageKey: 'simplechat.profile.publicWorkspaces.viewMode',
emptyMessage: 'No public workspaces found for the current search.',
loadingMessage: 'Loading public workspaces...',
discoverEmptyMessage: 'No public workspaces found for the current search.',
emptyMessage: `No ${publicWorkspaceLowerPlural} found for the current search.`,
loadingMessage: `Loading ${publicWorkspaceLowerPlural}...`,
discoverEmptyMessage: `No ${publicWorkspaceLowerPlural} found for the current search.`,
requestLabel: 'Request Access',
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ let currentStatsWindow = { days: 30, startDate: '', endDate: '' };
let currentStatsData = null;
const defaultWorkspaceHeroColor = '#0078d4';
const workspaceHeroColorPattern = /^#[0-9a-fA-F]{6}$/;
const publicWorkspaceSingular = window.getPublicWorkspaceLabel ? window.getPublicWorkspaceLabel('singular') : 'Public Workspace';
const publicWorkspaceLowerSingular = window.getPublicWorkspaceLabel ? window.getPublicWorkspaceLabel('lower_singular') : 'public workspace';

function normalizeWorkspaceHeroColor(color) {
const candidate = String(color || '').trim();
Expand Down Expand Up @@ -283,7 +285,7 @@ $(document).ready(function () {
`);
$("#deleteWorkspaceWarningModal").modal("show");
} else {
if (!confirm("Permanently delete this public workspace?")) return;
if (!confirm(`Permanently delete this ${publicWorkspaceLowerSingular}?`)) return;
$.ajax({
url: `/api/public_workspaces/${workspaceId}`,
method: "DELETE",
Expand Down Expand Up @@ -1025,7 +1027,7 @@ async function exportWorkspaceStats() {
const windowLabel = stats.window?.label || getStatsWindowLabel(exportWindow);
const rows = [];

rows.push('Public Workspace Stats Export');
rows.push(`${publicWorkspaceSingular} Stats Export`);
appendCsvRow(rows, ['Export Date', new Date().toLocaleString()]);
appendCsvRow(rows, ['Data Period', windowLabel]);
appendCsvSectionBreak(rows);
Expand Down Expand Up @@ -1077,10 +1079,10 @@ async function exportWorkspaceStats() {
if (modal) {
modal.hide();
}
showStatsToast('Public workspace stats exported successfully.', 'success');
showStatsToast(`${publicWorkspaceSingular} stats exported successfully.`, 'success');
} catch (error) {
console.error('Failed to export public workspace stats:', error);
showStatsToast('Failed to export public workspace stats.', 'danger');
showStatsToast(`Failed to export ${publicWorkspaceLowerSingular} stats.`, 'danger');
} finally {
if (exportButton) {
exportButton.disabled = false;
Expand Down
Loading
Loading