Skip to content

Commit d0d5e60

Browse files
authored
Development (#445)
* Update release notes to show support for GPT-5 * Documented support for gpt-image-1 * Update config.py * remove documentation folder * Documentation and message table support (#444) * Develop demo docs and import markdown table support * fixed enhanced citations for groups and public workspaces * Updated to support showing public workspaces in scope * Update config.py * fix docs * Updated RELEASE_NOTES
1 parent b381f82 commit d0d5e60

File tree

44 files changed

+7650
-103
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+7650
-103
lines changed

RELEASE_NOTES.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,41 @@
11
<!-- BEGIN RELEASE_NOTES.MD BLOCK -->
22
# Feature Release
33

4+
### **(v0.229.014)**
5+
6+
#### Bug Fixes
7+
8+
##### Public Workspace Management Fixes
9+
10+
* **Public Workspace Management Permission Fix**
11+
* Fixed incorrect permission checking for public workspace management operations when "Require Membership to Create Public Workspaces" setting was enabled.
12+
* **Issue**: Users with legitimate access to manage workspaces (Owner/Admin/DocumentManager) were incorrectly shown "Forbidden" errors when accessing management functionality.
13+
* **Root Cause**: The `manage_public_workspace` route was incorrectly decorated with `@create_public_workspace_role_required`, conflating creation permissions with management permissions.
14+
* **Solution**: Removed the incorrect permission decorator from the management route, allowing workspace-specific membership roles to properly control access.
15+
* (Ref: `route_frontend_public_workspaces.py`, workspace permission logic)
16+
17+
* **Public Workspace Scope Display Enhancement**
18+
* Enhanced the Public Workspace scope selector in chat interface to show specific workspace names instead of generic "Public" label.
19+
* **Display Logic**:
20+
* No visible workspaces: `"Public"`
21+
* 1 visible workspace: `"Public: [Workspace Name]"`
22+
* 2-3 visible workspaces: `"Public: [Name1], [Name2], [Name3]"`
23+
* More than 3 workspaces: `"Public: [Name1], [Name2], [Name3], 3+"`
24+
* **Benefits**: Improved workspace identification, consistent with Group scope naming pattern, better navigation between workspace scopes.
25+
* (Ref: `chat-documents.js`, scope label updates, dynamic workspace display)
26+
27+
##### User Interface and Content Rendering Fixes
28+
29+
* **Unicode Table Rendering Fix**
30+
* Fixed issue where AI-generated tables using Unicode box-drawing characters were not rendering as proper HTML tables in the chat interface.
31+
* **Problem**: AI agents (particularly ESAM Agent) generated Unicode tables that appeared as plain text instead of formatted tables.
32+
* **Solution**:
33+
* Added `convertUnicodeTableToMarkdown()` function to detect and convert Unicode table patterns to markdown format
34+
* Enhanced message processing pipeline to handle table preprocessing before markdown parsing
35+
* Improved `unwrapTablesFromCodeBlocks()` function to detect tables mistakenly wrapped in code blocks
36+
* **Impact**: Tables now render properly as HTML, improving readability and data presentation in chat responses.
37+
* (Ref: `chat-messages.js`, Unicode table conversion, markdown processing pipeline)
38+
439
### **(v0.229.001)**
540

641
#### New Features

application/single_app/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
EXECUTOR_TYPE = 'thread'
8989
EXECUTOR_MAX_WORKERS = 30
9090
SESSION_TYPE = 'filesystem'
91-
VERSION = "0.229.002"
91+
VERSION = "0.229.014"
9292
SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production')
9393

9494
CLIENTS = {}

application/single_app/route_enhanced_citations.py

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from functions_authentication import login_required, user_required, get_current_user_id
1313
from functions_settings import get_settings, enabled_required
1414
from functions_documents import get_document_metadata
15+
from functions_group import get_user_groups
16+
from functions_public_workspaces import get_user_visible_public_workspace_ids_from_settings
1517
from config import CLIENTS, storage_account_user_documents_container_name, storage_account_group_documents_container_name, storage_account_public_documents_container_name
1618

1719
def register_enhanced_citations_routes(app):
@@ -172,10 +174,59 @@ def get_enhanced_citation_pdf():
172174

173175
def get_document(user_id, doc_id):
174176
"""
175-
Get document metadata - using the existing function from functions_documents
177+
Get document metadata - searches across all enabled workspace types
176178
"""
177179
from functions_documents import get_document as backend_get_document
178-
return backend_get_document(user_id, doc_id)
180+
from functions_settings import get_settings
181+
182+
settings = get_settings()
183+
184+
# Try to get document from different workspace types based on what's enabled
185+
# Start with personal workspace (most common)
186+
if settings.get('enable_user_workspace', False):
187+
try:
188+
doc_response, status_code = backend_get_document(user_id, doc_id)
189+
if status_code == 200:
190+
return doc_response, status_code
191+
except:
192+
pass
193+
194+
# Try group workspaces if enabled
195+
if settings.get('enable_group_workspaces', False):
196+
# We need to find which group this document belongs to
197+
# This is more complex - we need to search across user's groups
198+
try:
199+
user_groups = get_user_groups(user_id)
200+
for group in user_groups:
201+
group_id = group.get('id')
202+
if group_id:
203+
try:
204+
doc_response, status_code = backend_get_document(user_id, doc_id, group_id=group_id)
205+
if status_code == 200:
206+
return doc_response, status_code
207+
except:
208+
continue
209+
except:
210+
pass
211+
212+
# Try public workspaces if enabled
213+
if settings.get('enable_public_workspaces', False):
214+
# We need to find which public workspace this document belongs to
215+
# This requires checking user's accessible public workspaces
216+
try:
217+
accessible_workspace_ids = get_user_visible_public_workspace_ids_from_settings(user_id)
218+
for workspace_id in accessible_workspace_ids:
219+
try:
220+
doc_response, status_code = backend_get_document(user_id, doc_id, public_workspace_id=workspace_id)
221+
if status_code == 200:
222+
return doc_response, status_code
223+
except:
224+
continue
225+
except:
226+
pass
227+
228+
# If document not found in any workspace
229+
return {"error": "Document not found or access denied"}, 404
179230

180231
def determine_workspace_type_and_container(raw_doc):
181232
"""
@@ -188,6 +239,17 @@ def determine_workspace_type_and_container(raw_doc):
188239
else:
189240
return 'personal', storage_account_user_documents_container_name
190241

242+
def get_blob_name(raw_doc, workspace_type):
243+
"""
244+
Determine the correct blob name based on workspace type
245+
"""
246+
if workspace_type == 'public':
247+
return f"{raw_doc['public_workspace_id']}/{raw_doc['file_name']}"
248+
elif workspace_type == 'group':
249+
return f"{raw_doc['group_id']}/{raw_doc['file_name']}"
250+
else:
251+
return f"{raw_doc['user_id']}/{raw_doc['file_name']}"
252+
191253
def serve_enhanced_citation_content(raw_doc, content_type=None):
192254
"""
193255
Server-side rendering: Serve enhanced citation file content directly
@@ -204,8 +266,8 @@ def serve_enhanced_citation_content(raw_doc, content_type=None):
204266
workspace_type, container_name = determine_workspace_type_and_container(raw_doc)
205267
container_client = blob_service_client.get_container_client(container_name)
206268

207-
# Build blob name
208-
blob_name = f"{raw_doc['user_id']}/{raw_doc['file_name']}"
269+
# Build blob name based on workspace type
270+
blob_name = get_blob_name(raw_doc, workspace_type)
209271

210272
try:
211273
# Download blob content directly
@@ -268,8 +330,8 @@ def serve_enhanced_citation_pdf_content(raw_doc, page_number):
268330
workspace_type, container_name = determine_workspace_type_and_container(raw_doc)
269331
container_client = blob_service_client.get_container_client(container_name)
270332

271-
# Build blob name
272-
blob_name = f"{raw_doc['user_id']}/{raw_doc['file_name']}"
333+
# Build blob name based on workspace type
334+
blob_name = get_blob_name(raw_doc, workspace_type)
273335

274336
try:
275337
# Download blob content directly

application/single_app/route_frontend_chats.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,6 @@ def upload_file():
165165
@app.route("/view_pdf", methods=["GET"])
166166
@login_required
167167
@user_required
168-
@enabled_required("enable_user_workspace")
169168
def view_pdf():
170169
"""
171170
1) Grab 'doc_id' and 'page' from query params.
@@ -191,12 +190,28 @@ def view_pdf():
191190
return doc_response, status_code
192191

193192
raw_doc = doc_response.get_json()
194-
blob_name = f"{raw_doc['user_id']}/{raw_doc['file_name']}"
193+
194+
# Determine workspace type and appropriate container
195+
settings = get_settings()
196+
if raw_doc.get('public_workspace_id'):
197+
if not settings.get('enable_public_workspaces', False):
198+
return jsonify({"error": "Public workspaces are not enabled"}), 403
199+
container_name = storage_account_public_documents_container_name
200+
blob_name = f"{raw_doc['public_workspace_id']}/{raw_doc['file_name']}"
201+
elif raw_doc.get('group_id'):
202+
if not settings.get('enable_group_workspaces', False):
203+
return jsonify({"error": "Group workspaces are not enabled"}), 403
204+
container_name = storage_account_group_documents_container_name
205+
blob_name = f"{raw_doc['group_id']}/{raw_doc['file_name']}"
206+
else:
207+
if not settings.get('enable_user_workspace', False):
208+
return jsonify({"error": "User workspaces are not enabled"}), 403
209+
container_name = storage_account_user_documents_container_name
210+
blob_name = f"{raw_doc['user_id']}/{raw_doc['file_name']}"
195211

196212
# 3) Generate the SAS URL (short-lived, read-only)
197-
settings = get_settings()
198213
blob_service_client = CLIENTS.get("storage_account_office_docs_client")
199-
container_client = blob_service_client.get_container_client(storage_account_user_documents_container_name)
214+
container_client = blob_service_client.get_container_client(container_name)
200215

201216
sas_token = generate_blob_sas(
202217
account_name=blob_service_client.account_name,
@@ -304,7 +319,6 @@ def view_pdf():
304319
@app.route('/view_document')
305320
@login_required
306321
@user_required
307-
@enabled_required("enable_user_workspace")
308322
def view_document():
309323
settings = get_settings()
310324
download_location = tempfile.gettempdir()
@@ -333,8 +347,22 @@ def view_document():
333347
if not file_name:
334348
return jsonify({"error": "Internal server error: Document metadata incomplete."}), 500
335349

336-
# Construct blob name using the owner's user_id from the document record
337-
blob_name = f"{owner_user_id}/{file_name}"
350+
# Determine workspace type and appropriate container
351+
if raw_doc.get('public_workspace_id'):
352+
if not settings.get('enable_public_workspaces', False):
353+
return jsonify({"error": "Public workspaces are not enabled"}), 403
354+
container_name = storage_account_public_documents_container_name
355+
blob_name = f"{raw_doc['public_workspace_id']}/{file_name}"
356+
elif raw_doc.get('group_id'):
357+
if not settings.get('enable_group_workspaces', False):
358+
return jsonify({"error": "Group workspaces are not enabled"}), 403
359+
container_name = storage_account_group_documents_container_name
360+
blob_name = f"{raw_doc['group_id']}/{file_name}"
361+
else:
362+
if not settings.get('enable_user_workspace', False):
363+
return jsonify({"error": "User workspaces are not enabled"}), 403
364+
container_name = storage_account_user_documents_container_name
365+
blob_name = f"{owner_user_id}/{file_name}"
338366
file_ext = os.path.splitext(file_name)[-1].lower()
339367

340368
# Ensure download location exists (good practice, especially if using mount)
@@ -349,7 +377,6 @@ def view_document():
349377
blob_service_client = CLIENTS.get("storage_account_office_docs_client")
350378
storage_account_key = settings.get("office_docs_key")
351379
storage_account_name = blob_service_client.account_name # Get from client
352-
container_name = storage_account_user_documents_container_name # From config
353380

354381
if not all([blob_service_client, storage_account_key, container_name]):
355382
return jsonify({"error": "Internal server error: Storage access not configured."}), 500

application/single_app/route_frontend_public_workspaces.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ def my_public_workspaces():
3131
@login_required
3232
@user_required
3333
@enabled_required("enable_public_workspaces")
34-
@create_public_workspace_role_required
3534
def manage_public_workspace(workspace_id):
3635
settings = get_settings()
3736
public_settings = sanitize_settings_for_user(settings)
@@ -98,4 +97,18 @@ def public_directory():
9897
'public_directory.html',
9998
settings=public_settings,
10099
app_settings=public_settings
101-
)
100+
)
101+
102+
@app.route('/set_active_public_workspace', methods=['POST'])
103+
@login_required
104+
@user_required
105+
@enabled_required("enable_public_workspaces")
106+
def set_active_public_workspace():
107+
user_id = get_current_user_id()
108+
workspace_id = request.form.get("workspace_id")
109+
if not user_id or not workspace_id:
110+
return "Missing user or workspace id", 400
111+
success = update_user_settings(user_id, {"activePublicWorkspaceOid": workspace_id})
112+
if not success:
113+
return "Failed to update user settings", 500
114+
return redirect(url_for('public_workspaces'))

0 commit comments

Comments
 (0)