Skip to content

Tabular, Thoughts, PDF Export, SQL plugin updates, and bug fixes#782

Open
paullizer wants to merge 10 commits intoDevelopmentfrom
Thought-and-fixes
Open

Tabular, Thoughts, PDF Export, SQL plugin updates, and bug fixes#782
paullizer wants to merge 10 commits intoDevelopmentfrom
Thought-and-fixes

Conversation

@paullizer
Copy link
Contributor

New Features

  • Persistent Conversation Summaries

    • Summaries generated during conversation export are now saved to the conversation document in Cosmos DB for future reuse.
    • Cached summaries include message_time_start and message_time_end — when a conversation has new messages beyond the cached range, a fresh summary is generated automatically.
    • The conversation details modal now shows a Summary card at the top. If a summary exists it displays the content, generation date, and model used. If no summary exists a Generate Summary button with model selector lets users create one on demand.
    • A Regenerate button is available on existing summaries to force a refresh with the currently selected model.
    • New POST /api/conversations/<id>/summary endpoint accepts an optional model_deployment and returns the generated summary.
    • The GET /api/conversations/<id>/metadata response now includes a summary field.
    • Extracted generate_conversation_summary() as a shared helper used by both the export pipeline and the new API endpoint.
    • (Ref: route_backend_conversation_export.py, route_backend_conversations.py, chat-conversation-details.js, functions_conversation_metadata.py)
  • PDF Conversation Export

    • Added PDF as a third export format option alongside JSON and Markdown, giving users a print-ready, visually styled conversation archive.
    • PDF output renders chat messages with colored bubbles that mirror the live chat UI: blue for user messages, gray for assistant messages, green for file messages, and amber for system messages.
    • Message content is converted from Markdown to HTML for rich formatting (bold, italic, code blocks, lists, tables) inside the PDF.
    • Full appendix structure is included (metadata, message details, references, processing thoughts, supplemental messages), matching the Markdown export layout.
    • Rendering uses PyMuPDF's Story API on US Letter paper with 0.5-inch margins and automatic multi-page overflow.
    • Works with both single-file and ZIP packaging; intro summaries are supported in PDF as well.
    • Frontend format step updated to a 3-column card grid with a new PDF card using the bi-filetype-pdf icon.
    • (Ref: route_backend_conversation_export.py, chat-export.js, PyMuPDF Story API, conversation export workflow)
  • Conversation Export Intro Summaries

    • Added an optional AI-generated intro summary step to the conversation export workflow, so each exported chat can begin with a short abstract before the full transcript.
    • Summary model selection now reuses the same model list shown in the chat composer, keeping the export flow aligned with the main chat experience.
    • Works for both JSON and Markdown exports, including ZIP exports where each conversation keeps its own summary metadata.
    • (Ref: route_backend_conversation_export.py, chat-export.js, conversation export workflow)
  • Agent & Action User Tracking (created_by / modified_by)

    • All agent and action documents (personal, group, and global) now include created_by, created_at, modified_by, and modified_at fields that track which user created or last modified the entity.
    • On updates, the original created_by and created_at values are preserved while modified_by and modified_at are refreshed with the current user and timestamp.
    • New optional user_id parameter added to save_group_agent, save_global_agent, save_group_action, and save_global_action for caller-supplied user tracking (backward-compatible, defaults to None).
    • (Ref: functions_personal_agents.py, functions_group_agents.py, functions_global_agents.py, functions_personal_actions.py, functions_group_actions.py, functions_global_actions.py)
  • Activity Logging for Agent & Action CRUD Operations

    • Every create, update, and delete operation on agents and actions now generates an activity log record in the activity_logs Cosmos DB container and Application Insights.
    • Six new logging functions: log_agent_creation, log_agent_update, log_agent_deletion, log_action_creation, log_action_update, log_action_deletion.
    • Activity records include: user_id, activity_type, entity_type (agent/action), operation (create/update/delete), workspace_type (personal/group/global), and workspace_context (group_id when applicable).
    • Logging is fire-and-forget — failures never break the CRUD operation.
    • All personal, group, and admin routes for both agents and actions are wired up.
    • (Ref: functions_activity_logging.py, route_backend_agents.py, route_backend_plugins.py)
  • Tabular Data Analysis — SK Mini-Agent for Normal Chat

    • Tabular files (CSV, XLSX, XLS, XLSM) detected in search results now trigger a lightweight Semantic Kernel mini-agent that pre-computes data analysis before the main LLM response. This brings the same analytical depth previously only available in full agent mode to every normal chat conversation.
    • Automatic Detection: When AI Search results include tabular files from any workspace (personal, group, or public) or chat-uploaded documents, the system automatically identifies them via the TABULAR_EXTENSIONS configuration and routes the query through the SK mini-agent pipeline.
    • Unified Workspace and Chat Handling: Tabular files are processed identically regardless of their storage location. The plugin resolves blob paths across all four container types (user-documents, group-documents, public-documents, personal-chat) with automatic fallback resolution if the primary source lookup fails. A user asking about an Excel file in their personal workspace gets the same analytical treatment as one asking about a CSV uploaded directly to a chat.
    • Six Data Analysis Functions: The TabularProcessingPlugin exposes describe_tabular_file, aggregate_column (sum, mean, count, min, max, median, std, nunique, value_counts), filter_rows (==, !=, >, <, >=, <=, contains, startswith, endswith), query_tabular_data (pandas query syntax), group_by_aggregate, and list_tabular_files — all registered as Semantic Kernel functions that the mini-agent orchestrates autonomously.
    • Pre-Computed Results Injected as Context: The mini-agent's computed analysis (exact numerical results, aggregations, filtered data) is injected into the main LLM's system context so it can present accurate, citation-backed answers without hallucinating numbers.
    • Graceful Degradation: If the mini-agent analysis fails for any reason, the system falls back to instructing the main LLM to use the tabular processing plugin functions directly, preserving full functionality.
    • Non-Streaming and Streaming Support: Both chat modes are supported. The mini-agent runs synchronously before the main LLM call in both paths.
    • Requires Enhanced Citations: The tabular processing plugin depends on the blob storage client initialized by the enhanced citations system. The enable_enhanced_citations admin setting must be enabled for tabular data analysis to activate.
    • (Ref: run_tabular_sk_analysis(), TabularProcessingPlugin, collect_tabular_sk_citations(), TABULAR_EXTENSIONS)
  • Tabular Tool Execution Citations

    • Every tool call made by the SK mini-agent during tabular analysis is captured and surfaced as an agent citation, providing full transparency into the data analysis pipeline.
    • Automatic Capture: The existing @plugin_function_logger decorator on all TabularProcessingPlugin functions records each invocation including function name, input parameters, returned results, execution duration, and success/failure status.
    • Citation Format: Tool execution citations appear in the same "Agent Tool Execution" modal used by full agent mode, showing tool_name (e.g., TabularProcessingPlugin.aggregate_column), function_arguments (the exact parameters passed), and function_result (the computed data returned).
    • End-to-End Auditability: Users can verify exactly which aggregations, filters, or queries were run against their data, what parameters were used, and what raw results were returned — before the LLM summarized them into the final response.
    • (Ref: collect_tabular_sk_citations(), plugin_invocation_logger.py)
  • SK Mini-Agent Performance Optimization

    • Reduced typical tabular analysis time from ~74 seconds to an estimated ~30-33 seconds (55-60% reduction) through three complementary optimizations.
    • DataFrame Caching: Per-request in-memory cache eliminates redundant blob downloads. Previously, each of the ~8 tool calls in a typical analysis downloaded and parsed the same file independently. Now the file is downloaded once and subsequent calls read from cache. Cache is automatically scoped to the request (new plugin instance per analysis) and garbage-collected afterward.
    • Pre-Dispatch Schema Injection: File schemas (columns, data types, row counts, and a 3-row preview) are pre-loaded and injected into the SK mini-agent's system prompt before execution begins. This eliminates 2 LLM round-trips that were previously spent on file discovery (list_tabular_files) and schema inspection (describe_tabular_file), allowing the model to jump directly to analysis tool calls.
    • Async Plugin Functions: All six @kernel_function methods converted to async def using asyncio.to_thread(). This enables Semantic Kernel's built-in asyncio.gather() to truly parallelize batched tool calls (e.g., 3 simultaneous aggregate_column calls) instead of executing them serially on the event loop.
    • Batching Instructions: The system prompt now instructs the model to batch multiple independent function calls in a single response, reducing LLM round-trips further.
    • (Ref: _df_cache, asyncio.to_thread, pre-dispatch schema injection in run_tabular_sk_analysis())
  • SQL Test Connection Button

    • Added a "Test Connection" button to the SQL Database Configuration section (Step 3) of the action wizard, allowing users to validate database connectivity before saving.
    • Supports all database types: SQL Server, Azure SQL (with managed identity), PostgreSQL, MySQL, and SQLite.
    • Shows inline success/failure alerts with a 15-second timeout cap and sanitized error messages.
    • New backend endpoint: POST /api/plugins/test-sql-connection.
    • (Ref: route_backend_plugins.py, plugin_modal_stepper.js, _plugin_modal.html)

Bug Fixes

  • On-Demand Summary Generation — Content Normalization Fix

    • Fixed the POST /api/conversations/<id>/summary endpoint failing with an error when generating summaries from the conversation details modal.
    • Root cause: message content in Cosmos DB can be a list of content parts (e.g., [{type: "text", text: "..."}]) rather than a plain string. The endpoint was passing the raw list as content_text, which either stringified incorrectly or produced empty transcript text.
    • Now uses _normalize_content() to properly flatten list/dict content into plain text, matching the export pipeline's behavior.
    • (Ref: route_backend_conversations.py, _normalize_content, generate_conversation_summary)
  • Export Summary Reasoning-Model Compatibility

    • Fixed export intro summary generation failing or returning empty content with reasoning-series models (gpt-5, o1, o3) through a series of incremental fixes: using developer role instead of system for instruction messages, removing all max_tokens / max_completion_tokens caps so the model decides output length naturally, and adding null-safe content extraction for None responses.
    • Summary now includes ALL messages (user, assistant, system, file, image analysis) for full context, with a simplified prompt producing 1-2 factual paragraphs.
    • Added detailed debug logging showing message count, character count, model name, role, and finish reason.
    • (Ref: route_backend_conversation_export.py, _build_summary_intro, generate_conversation_summary)
  • Conversation Export Schema and Markdown Refresh

    • Fixed conversation exports lagging behind the live chat schema. JSON exports now include processing thoughts, normalized citations, and the raw document/web/tool citation buckets stored with assistant messages.
    • Fixed Markdown exports being too flat and text-heavy by reorganizing them into a transcript-first layout with appendices for metadata, message details, references, thoughts, and supplemental records.
    • Fixed exported conversations including content that no longer matched the visible chat by filtering deleted messages and inactive-thread retries, then reapplying thread-aware ordering before export.
    • (Ref: route_backend_conversation_export.py, test_conversation_export.py, conversation export rendering)
  • Export Tag/Classification Rendering Fix

    • Fixed conversation tags and classifications rendering as raw Python dicts (e.g., {'category': 'model', 'value': 'gpt-5'}) in both Markdown and PDF exports.
    • Tags now display as readable category: value strings, with smart handling for participant names, document titles, and generic category/value pairs.
    • (Ref: route_backend_conversation_export.py, _format_tag helper, Markdown/PDF metadata rendering)
  • Export Summary Error Visibility

    • Added debug_print and log_event logging to all summary generation error paths, including the empty-response path that previously failed silently.
    • The actual error detail is now shown in both Markdown and PDF exports when summary generation fails, replacing the generic "could not be generated" message.
    • (Ref: route_backend_conversation_export.py, _build_summary_intro, export error rendering)
  • Content Safety for Streaming Chat Path

    • Added full Azure AI Content Safety checking to the streaming (/api/chat/stream) SSE path, matching the existing non-streaming (/api/chat) implementation.
    • Previously, only the non-streaming path performed content safety analysis; streaming conversations bypassed safety checks entirely.
    • Implementation includes: AnalyzeTextOptions analysis, severity threshold checking (severity ≥ 4 blocks the message), blocklist matching, persistence of blocked messages to cosmos_safety_container, creation of safety-role message documents, and proper SSE event delivery of blocked status to the client.
    • On block, the streaming generator yields the safety message and [DONE] event, then stops — preventing any further LLM invocation.
    • Errors in the content safety call are caught and logged without breaking the chat flow, consistent with the non-streaming behavior.
    • (Ref: route_backend_chats.py, streaming SSE generator, AnalyzeTextOptions, cosmos_safety_container)
  • SQL Schema Plugin — Eliminate Redundant Schema Calls

    • Fixed agent calling get_database_schema twice per query even though the full schema was already injected into the agent's instructions at load time.
    • Root cause: The @kernel_function descriptions in sql_schema_plugin.py said "ALWAYS call this function FIRST," which overrode the schema context already available in the instructions.
    • Updated all four function descriptions (get_database_schema, get_table_schema, get_table_list, get_relationships) to use the resilient pattern: "If the database schema is already provided in your instructions, use that directly and do NOT call this function."
    • This eliminates ~400ms+ of unnecessary database round trips per query and aligns with the same pattern already used in sql_query_plugin.py.
    • (Ref: sql_schema_plugin.py, @kernel_function descriptions, schema injection)
  • SQL Schema Plugin — Empty Tables from INFORMATION_SCHEMA

    • Fixed get_database_schema returning 'tables': {} (empty) despite the database having tables, while relationships were returned correctly.
    • Root cause: SQL Server table/column enumeration used INFORMATION_SCHEMA.TABLES and INFORMATION_SCHEMA.COLUMNS views, which returned empty results in the Azure SQL environment. Meanwhile, the relationships query used sys.foreign_keys/sys.tables/sys.columns catalog views which worked perfectly.
    • Migrated all SQL Server schema queries to use sys.* catalog views consistently: sys.tables/sys.schemas for table enumeration, sys.columns with TYPE_NAME() for column details, and sys.indexes/sys.index_columns for primary key detection.
    • Fixed pyodbc.Row handling throughout the plugin — removed all isinstance(table, tuple) checks that could fail with pyodbc Row objects, replaced with robust try/except indexing.
    • This enables the full schema (tables, columns, types, PKs, FKs) to be injected into agent instructions, allowing agents to construct complex multi-table JOINs for analytical queries.
    • (Ref: sql_schema_plugin.py, sys.tables, sys.columns, sys.indexes, pyodbc.Row handling)
  • SQL Query Plugin — Auto-Create Companion Schema Plugin

    • Fixed the remaining issue where SQL-connected agents still asked for clarification instead of querying the database, even after description improvements.
    • Root cause: Agents configured with only a sql_query action never had a SQLSchemaPlugin loaded in the kernel. The descriptions demanded calling get_database_schema — a function that didn't exist — creating an impossible dependency that caused the LLM to ask for clarification.
    • LoggedPluginLoader now automatically creates a companion SQLSchemaPlugin whenever a SQLQueryPlugin is loaded, using the same connection details. This ensures schema discovery is always available.
    • Updated @kernel_function descriptions to be resilient: "If the database schema is provided in your instructions, use it directly. Otherwise, call get_database_schema." This dual-path approach works whether schema is injected via instructions or available via plugin functions.
    • Added fallback in _extract_sql_schema_for_instructions() to also detect SQLQueryPlugin instances and create a temporary schema extractor if no SQLSchemaPlugin is found.
    • (Ref: logged_plugin_loader.py, sql_query_plugin.py, semantic_kernel_loader.py)
  • SQL Query Plugin Schema Awareness

    • Fixed agents connected to SQL databases asking users for clarification about table/column names instead of querying the database directly.
    • Root cause: SQL Query and SQL Schema plugin @kernel_function descriptions were generic with no workflow guidance, agent instructions had no database schema context, and the two plugins operated independently with no linkage.
    • Rewrote all @kernel_function descriptions in both SQL plugins to be prescriptive workflow guides (modeled after the working LogAnalyticsPlugin), explicitly instructing the LLM to discover schema first before generating queries.
    • Added auto-injection of database schema into agent instructions at load time — when SQL Schema plugins are detected, the full schema (tables, columns, types, relationships) is fetched and appended to the agent's system prompt.
    • Added new query_database(question, query) convenience function to SQLQueryPlugin for intent-aligned tool calling.
    • Enabled the SQL-specific plugin creation path in logged_plugin_loader.py (was previously commented out).
    • (Ref: sql_query_plugin.py, sql_schema_plugin.py, semantic_kernel_loader.py, logged_plugin_loader.py)
  • Chat-Uploaded Tabular Files Now Trigger SK Mini-Agent in Model-Only Mode

    • Fixed an issue where tabular files (CSV, XLSX, XLS, XLSM) uploaded directly to a chat conversation were not analyzed by the SK mini-agent when no agent was selected. The model would describe what analysis it would perform instead of returning actual computed results.
    • Root Cause: The mini SK agent only triggered from search results, but chat-uploaded files are stored in blob storage and not indexed in Azure AI Search. Additionally, the streaming path completely ignored file role messages in conversation history.
    • Fix: Both streaming and non-streaming chat paths now detect chat-uploaded tabular files during conversation history building and trigger run_tabular_sk_analysis(source_hint="chat") to pre-compute results. The streaming path also now properly handles file role messages (tabular and non-tabular) matching the non-streaming path's behavior.
    • (Ref: route_backend_chats.py, run_tabular_sk_analysis(), collect_tabular_sk_citations())
  • Group SQL Action/Plugin Save Failure

    • Fixed group SQL actions (sql_query and sql_schema types) failing to save correctly due to missing endpoint placeholder. Group routes now apply the same sql://sql_query / sql://sql_schema endpoint logic as personal action routes.
    • Fixed Step 4 (Advanced) dynamic fields overwriting Step 3 (Configuration) SQL values with empty strings during form data collection. SQL types now skip the dynamic field merge entirely since Step 3 already provides all necessary configuration.
    • Fixed auth type definition schemas (sql_query.definition.json, sql_schema.definition.json) only allowing connection_string auth type, blocking user, identity, and servicePrincipal types that the UI and runtime support.
    • Fixed __Secret key suffix mismatch in additional settings schemas where connection_string__Secret and password__Secret didn't match the runtime's expected connection_string and password field names. Also removed duplicate azuresql enum value.
    • (Ref: route_backend_plugins.py, plugin_modal_stepper.js, sql_query.definition.json, sql_schema.definition.json, sql_query_plugin.additional_settings.schema.json, sql_schema_plugin.additional_settings.schema.json)

User Interface Enhancements

  • Agent Responded Thought — Seconds & Total Duration

    • The "responded" thought now shows time in seconds instead of milliseconds, and clarifies it is the total time from the initial user message (e.g., 'gpt-5-nano' responded (16.3s from initial message)).
    • A request_start_time is now captured at the top of both the non-streaming and streaming chat handlers, so the duration reflects the full request lifecycle — including content safety, hybrid search, and agent invocation — not just the model response time.
    • Applies to all three agent paths: local SK agents (non-streaming), Azure AI Foundry agents, and streaming SK agents.
    • (Ref: route_backend_chats.py, request_start_time, agent responded thoughts)
  • Enhanced Agent Execution Thoughts

    • Added detailed model-level status messages during agent execution, giving users full visibility into each stage of the AI pipeline.
    • Model Identification: A new "Sending to '{deployment_name}'" thought appears immediately after "Sending to agent", showing the exact model deployment being used (e.g., gpt-5-nano).
    • Generating Response: A "Generating response..." thought now appears before the agent begins its invocation loop, matching the existing behavior for non-agent GPT calls.
    • Model Responded with Duration: A "'{deployment_name}' responded ({duration}ms)" thought appears after the agent completes, showing total wall-clock execution time.
    • Applies to all three agent paths: local SK agents (streaming and non-streaming) and Azure AI Foundry agents.
    • Uses the existing generation step type (lightning bolt icon) — no frontend changes required.
    • (Ref: route_backend_chats.py, ThoughtTracker, agent execution pipeline)
  • List/Grid View Toggle for Agents and Actions

    • Added a list/grid view toggle to all four workspace areas: personal agents, personal actions, group agents, and group actions.
    • Grid View: Large cards with type icon, humanized name, truncated description, and action buttons (Chat, View, Edit, Delete as applicable).
    • List View: Improved table layout with fixed column widths (28%/47%/25%), humanized display names, and truncated descriptions with hover tooltips for full text.
    • View Button: New eye-icon button on every agent and action that opens a read-only detail modal with gradient-header summary cards (Basic Information, Model Configuration, Instructions for agents; Basic Information, Configuration for actions).
    • Name Humanization: Display names are now automatically parsed — underscores and camelCase/PascalCase boundaries are converted to properly spaced, title-cased words (e.g., myCustomAgentMy Custom Agent).
    • Persistent Preference: View mode selection (list/grid) is saved per area in localStorage and restored on page load.
    • New shared utility module view-utils.js provides reusable functions for all four workspace areas.
    • (Ref: view-utils.js, workspace_agents.js, workspace_plugins.js, plugin_common.js, group_agents.js, group_plugins.js, workspace.html, group_workspaces.html, styles.css)
  • Chat with Agent Button for Group Agents

    • Added a "Chat" button to each group agent row, allowing users to quickly select a group agent and navigate to the chat page.
    • (Ref: group_agents.js, group_workspaces.html)
  • Hidden Deprecated Action Types

    • Deprecated action types (sql_schema, ui_test, queue_storage, blob_storage, embedding_model) are now hidden from the action creation wizard type selector. Existing actions of these types remain functional.
    • (Ref: plugin_modal_stepper.js)
  • Advanced Settings Collapse Toggle

    • Step 4 (Advanced) content is now hidden behind a collapsible toggle button ("Show Advanced Settings") instead of being displayed by default. Reduces visual noise for most users.
    • For SQL action types, the redundant additional fields UI in Step 4 is hidden entirely since all SQL configuration is already handled in Step 3.
    • Step 5 (Summary) no longer shows the raw additional fields JSON dump for SQL types, since that data is already shown in the SQL Database Configuration summary card.
    • (Ref: _plugin_modal.html, plugin_modal_stepper.js)

* initial

* adding logging
* Improve SQL action ui and config

* adding user_id and created by modified by to group and global agents/actions and activity logs for all agent/action CRUD tasks
* initial tabular data support

* working with workspaces, aligned with chat

* add popup modal view for xlsx

* added tabular data analysis as a mini-agent within chats to process tab data without full agent

* Update route_backend_chats.py

* SK Mini-Agent Performance Optimization

* fixed inline sk trigger bug
* Improve SQL action ui and config

* adding user_id and created by modified by to group and global agents/actions and activity logs for all agent/action CRUD tasks

* schema is injected directly into the agent's brain at load time, and descriptions explicitly instruct the LLM on the correct tool-calling workflow.
Copilot AI review requested due to automatic review settings March 9, 2026 13:43
@paullizer paullizer changed the title Tabular, Thoughts, PDF Export, and bug fixes Tabular, Thoughts, PDF Export, SQL plugin updates, and bug fixes Mar 9, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is a broad feature+fix bundle that expands conversation export capabilities (PDF + intro summaries + persistent cached summaries), adds “Processing Thoughts” end-to-end (admin toggle → persistence → UI), improves workspace agent/action UX (list/grid + view modal), and hardens SQL plugin behavior (schema awareness + companion schema plugin + sys.* catalog view migration).

Changes:

  • Add persistent conversation summaries (new API + UI surface) and enhance export workflow (PDF format + optional intro summary step).
  • Add Processing Thoughts (settings, Cosmos containers, backend routes, streaming + polling UI).
  • Improve SQL plugins and enhanced citations handling for tabular files (download/preview + chat-upload blob storage), plus workspace list/grid/view UX updates.

Reviewed changes

Copilot reviewed 74 out of 77 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
functional_tests/test_sql_schema_sys_catalog_views.py Adds functional coverage for SQL Server sys.* schema queries (but currently hard-codes an older VERSION).
functional_tests/test_sql_query_plugin_schema_awareness.py Adds functional coverage for SQL plugin workflow guidance (currently asserts outdated strings / version header).
functional_tests/test_sql_auto_schema_companion.py Adds functional coverage for auto-creating SQLSchemaPlugin companion (not verified via tools in this environment).
functional_tests/test_persistent_conversation_summary.py Adds functional coverage for cached summary intro behavior (version header currently out of sync).
docs/explanation/fixes/v0.239.008/CHAT_TABULAR_SK_TRIGGER_FIX.md Fix documentation for chat-uploaded tabular SK trigger behavior.
docs/explanation/fixes/SQL_QUERY_PLUGIN_SCHEMA_AWARENESS_FIX.md Fix documentation for SQL schema awareness + companion plugin + sys.* migration.
docs/explanation/features/v0.239.022/CONVERSATION_EXPORT.md Feature documentation snapshot for updated export shape/workflow.
docs/explanation/features/v0.239.003/PROCESSING_THOUGHTS.md Feature documentation for Processing Thoughts.
application/single_app/templates/workspace.html Adds list/grid view toggle and shared “item view” modal for personal workspace.
application/single_app/templates/group_workspaces.html Adds list/grid view toggle and shared “item view” modal for group workspace.
application/single_app/templates/chats.html Exposes enable_thoughts flag to frontend settings.
application/single_app/templates/admin_settings.html Adds tabular processing toggle + Processing Thoughts settings section.
application/single_app/templates/_sidebar_nav.html Adds admin sidebar jump link for Processing Thoughts section.
application/single_app/templates/_plugin_modal.html Adds SQL “Test Connection” UI and collapsible Advanced settings.
application/single_app/templates/_agent_examples_modal.html Renders template instructions as sanitized markdown instead of
.
application/single_app/static/json/schemas/sql_schema_plugin.additional_settings.schema.json Fixes schema key names and required fields for SQL schema additional settings.
application/single_app/static/json/schemas/sql_schema.definition.json Expands allowed auth types for sql_schema plugin.
application/single_app/static/json/schemas/sql_query_plugin.additional_settings.schema.json Fixes schema key names and required fields for SQL query additional settings.
application/single_app/static/json/schemas/sql_query.definition.json Expands allowed auth types for sql_query plugin.
application/single_app/static/js/workspace/workspace_plugins.js Adds grid rendering + view modal wiring for personal actions.
application/single_app/static/js/workspace/workspace_agents.js Adds list/grid rendering + view modal wiring for personal agents.
application/single_app/static/js/workspace/group_plugins.js Adds list/grid rendering + view modal wiring for group actions.
application/single_app/static/js/workspace/group_agents.js Adds list/grid rendering + view modal wiring + “Chat” for group agents.
application/single_app/static/js/plugin_common.js Adds view button + grid rendering shared helpers for plugins/actions.
application/single_app/static/js/chat/chat-thoughts.js New client module for thoughts polling, SSE thought handling, and per-message toggle rendering.
application/single_app/static/js/chat/chat-streaming.js Wires SSE thought events into the streaming UI.
application/single_app/static/js/chat/chat-messages.js Adds thoughts toggle + polling lifecycle to message send/receive flow.
application/single_app/static/js/chat/chat-loading-indicator.js Adds dynamic loading indicator text updates (currently has an XSS issue).
application/single_app/static/js/chat/chat-input-actions.js Adds “Download Original” for blob-backed chat file modal.
application/single_app/static/js/chat/chat-export.js Adds PDF option + summary intro step + summary model selection to export wizard.
application/single_app/static/js/chat/chat-enhanced-citations.js Adds tabular citation type + preview/download modal.
application/single_app/static/js/chat/chat-conversation-details.js Adds summary card UI + generate/regenerate summary flow.
application/single_app/static/js/admin/admin_settings.js Adds tabular toggle + thoughts gating to optional-features step logic.
application/single_app/static/css/styles.css Adds grid card + markdown rendering styles.
application/single_app/static/css/chats.css Adds Processing Thoughts UI styles (toggle, timeline, streaming badge).
application/single_app/semantic_kernel_plugins/sql_schema_plugin.py Migrates SQL Server schema discovery to sys.* + improves pyodbc.Row handling + updates descriptions.
application/single_app/semantic_kernel_plugins/sql_query_plugin.py Adds query_database + updates descriptions/metadata (metadata currently reintroduces “MUST” language).
application/single_app/semantic_kernel_plugins/plugin_invocation_logger.py Adds callback registry for invocation events.
application/single_app/semantic_kernel_plugins/logged_plugin_loader.py Enables SQL plugin creation path + auto-creates companion SQLSchemaPlugin for SQLQueryPlugin.
application/single_app/route_frontend_chats.py Stores chat-uploaded tabular files in blob storage when enhanced citations enabled; Cosmos message references blob.
application/single_app/route_frontend_admin_settings.py Saves enable_thoughts + defaults tabular processing toggle setting.
application/single_app/route_enhanced_citations.py Adds tabular download + workspace tabular download + tabular preview endpoints.
application/single_app/route_backend_user_agreement.py Allows workspace_type="chat".
application/single_app/route_backend_thoughts.py New API routes for per-message thoughts + pending thoughts polling.
application/single_app/route_backend_documents.py Reads blob-backed file messages for file content endpoint (no preview limits).
application/single_app/route_backend_conversations.py Adds summary to metadata response + new POST summary endpoint; archives/deletes thoughts on conversation delete.
application/single_app/route_backend_agents.py Adds activity logging and created_by/modified_by propagation for agents.
application/single_app/functions_thoughts.py Implements ThoughtTracker + thoughts CRUD + archive/delete helpers.
application/single_app/functions_settings.py Adds enable_thoughts + enable_tabular_processing_plugin defaults and dependency enforcement.
application/single_app/functions_personal_agents.py Adds created_by/modified_by tracking for personal agents.
application/single_app/functions_personal_actions.py Adds created_by/modified_by tracking for personal actions.
application/single_app/functions_group_agents.py Adds created_by/modified_by tracking + optional user_id param for group agents.
application/single_app/functions_group_actions.py Adds created_by/modified_by tracking + optional user_id param for group actions.
application/single_app/functions_global_agents.py Adds created_by/modified_by tracking + optional user_id param for global agents.
application/single_app/functions_global_actions.py Adds created_by/modified_by tracking for global actions (currently fails to default user_id).
application/single_app/functions_content.py Adds batched embedding generation helper and reduces per-call jitter delay.
application/single_app/config.py Bumps VERSION, adds chat blob containers, adds Cosmos containers for thoughts, ensures containers exist.
application/single_app/app.py Registers new thoughts backend routes.
README.md Documents SQL Database Agents optional feature.
CLAUDE.md Updates internal guidance wording for release notes/versioning.

Comment on lines +54 to +62
export function updateLoadingIndicatorText(text, iconClass) {
const textEl = document.getElementById("loading-indicator-text");
if (!textEl) return;

if (iconClass) {
textEl.innerHTML = `<i class="bi ${iconClass} me-1"></i>${text}`;
} else {
textEl.textContent = text;
}
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updateLoadingIndicatorText() uses innerHTML with unescaped text, but text ultimately contains user-controlled content (e.g., the user query echoed in ThoughtTracker step strings). This is an XSS vector. Prefer setting textContent (and render the icon via DOM nodes), or escape/sanitize text before inserting into innerHTML.

Copilot uses AI. Check for mistakes.
Comment on lines +197 to +221
conversation_id = request.args.get("conversation_id")
file_id = request.args.get("file_id")

if not conversation_id or not file_id:
return jsonify({"error": "conversation_id and file_id are required"}), 400

user_id = get_current_user_id()
if not user_id:
return jsonify({"error": "User not authenticated"}), 401

try:
# Look up the file message in Cosmos to get blob reference
query_str = """
SELECT * FROM c
WHERE c.conversation_id = @conversation_id
AND c.id = @file_id
"""
items = list(cosmos_messages_container.query_items(
query=query_str,
parameters=[
{'name': '@conversation_id', 'value': conversation_id},
{'name': '@file_id', 'value': file_id}
],
partition_key=conversation_id
))
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The /api/enhanced_citations/tabular endpoint looks up a file message by (conversation_id, file_id) and serves the blob, but it never verifies the current user owns the conversation (or otherwise has access). Any authenticated user who can obtain/guess these IDs could download another user's chat-uploaded files. Add an ownership check by reading the conversation doc and comparing conversation.user_id to get_current_user_id() before returning the blob.

Copilot uses AI. Check for mistakes.
Comment on lines +315 to +317
doc_id = request.args.get("doc_id")
max_rows = min(int(request.args.get("max_rows", 200)), 500)
if not doc_id:
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

max_rows is parsed via int(request.args.get('max_rows', 200)) without guarding ValueError, so a non-integer query param will raise and return 500. Consider using Flask's type=int parsing (like other routes do) or wrapping the int() conversion and returning a 400 on invalid input.

Copilot uses AI. Check for mistakes.
Comment on lines +2 to +6
"""
Functional test for SQL Query Plugin Schema Awareness improvements.
Version: 0.239.014
Implemented in: 0.239.014

Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional test header/version tracking is out of sync with the repo version (config.py is now 0.239.031). Per repo version-management guidelines, functional tests should include the current VERSION in the header so it's clear what release they apply to. Update the Version/Implemented in fields accordingly.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +185 to +197
def test_version_updated():
"""Test that the version has been updated for this fix."""
print("🔍 Testing version update...")

try:
config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
'..', 'application', 'single_app', 'config.py')
with open(config_path, 'r') as f:
config_content = f.read()

assert 'VERSION = "0.239.016"' in config_content, \
f"config.py should contain VERSION = \"0.239.016\""

Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test_version_updated() hard-codes VERSION = "0.239.016", but config.py in this PR is bumped to 0.239.031. This will fail as-is. Either update the asserted version to the current VERSION or avoid hard-coding a specific patch version in the test (e.g., parse VERSION from config.py and assert it's non-empty / matches expected format).

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +3 to +7
"""
Functional test for persistent conversation summaries.
Version: 0.239.030
Implemented in: 0.239.030

Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional test header lists Version/Implemented in as 0.239.030, but config.py is 0.239.031 in this PR. Update the header to the current VERSION per the repo’s version-management guidelines so the test is traceable to the release it ships with.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines 178 to +184
"This plugin executes SQL queries against databases and returns structured results. "
"It supports SQL Server, PostgreSQL, MySQL, and SQLite databases. The plugin includes "
"query sanitization, validation, and security features including parameterized queries, "
"read-only mode, result limiting, and timeout protection. It automatically cleans queries "
"from unnecessary characters and formats results for easy consumption by AI agents. "
"The plugin handles database-specific SQL variations and connection management."
"It supports SQL Server, PostgreSQL, MySQL, and SQLite databases. "
"WORKFLOW: Before executing any query, you MUST first use the SQL Schema plugin to discover "
"available tables, column names, data types, and relationships. Then construct valid SQL queries "
"using the discovered schema with correct fully-qualified table names (e.g., dbo.TableName). "
"The plugin includes query sanitization, validation, and security features including "
"parameterized queries, read-only mode, result limiting, and timeout protection."
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQLQueryPlugin.metadata() introduces a hard dependency ("you MUST first use the SQL Schema plugin") even though the kernel_function descriptions were updated to be resilient (use schema from instructions when present; call schema tools only when needed). This "MUST" wording can reintroduce the earlier failure mode when schema isn’t available. Consider aligning metadata wording with the resilient pattern used in the function descriptions.

Copilot uses AI. Check for mistakes.
Comment on lines +335 to +356
# Download blob
workspace_type, container_name = determine_workspace_type_and_container(raw_doc)
blob_name = get_blob_name(raw_doc, workspace_type)
blob_service_client = CLIENTS.get("storage_account_office_docs_client")
if not blob_service_client:
return jsonify({"error": "Blob storage client not available"}), 500
blob_client = blob_service_client.get_blob_client(container=container_name, blob=blob_name)
data = blob_client.download_blob().readall()

# Read into DataFrame
if ext == 'csv':
df = pandas.read_csv(io.BytesIO(data), keep_default_na=False, dtype=str)
elif ext in ('xlsx', 'xlsm'):
df = pandas.read_excel(io.BytesIO(data), engine='openpyxl', keep_default_na=False, dtype=str)
elif ext == 'xls':
df = pandas.read_excel(io.BytesIO(data), engine='xlrd', keep_default_na=False, dtype=str)
else:
return jsonify({"error": f"Unsupported file type: {ext}"}), 400

total_rows = len(df)
preview = df.head(max_rows)

Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tabular_preview downloads the entire blob into memory and then loads the entire dataset into a DataFrame, even though only a small preview is returned. For large CSV/XLSX this can be very slow and memory-intensive. Consider using nrows/max_rows in pandas.read_csv/read_excel (where supported), and/or a size cap on the blob download to keep this endpoint lightweight.

Copilot uses AI. Check for mistakes.
Comment on lines 63 to +100
@@ -76,8 +77,27 @@ def save_global_action(action_data):
action_data['id'] = str(uuid.uuid4())
# Add metadata
action_data['is_global'] = True
action_data['created_at'] = datetime.utcnow().isoformat()
action_data['updated_at'] = datetime.utcnow().isoformat()
now = datetime.utcnow().isoformat()

# Check if this is a new action or an update to preserve created_by/created_at
existing_action = None
try:
existing_action = cosmos_global_actions_container.read_item(
item=action_data['id'],
partition_key=action_data['id']
)
except Exception:
pass

if existing_action:
action_data['created_by'] = existing_action.get('created_by', user_id)
action_data['created_at'] = existing_action.get('created_at', now)
else:
action_data['created_by'] = user_id
action_data['created_at'] = now
action_data['modified_by'] = user_id
action_data['modified_at'] = now
action_data['updated_at'] = now
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

save_global_action() accepts user_id but never defaults it when None, which means created_by/modified_by will be persisted as null for callers that don't pass user_id (e.g., plugin_validation_endpoint calls save_global_action(plugin_manifest)). Mirror save_global_agent() by populating user_id from get_current_user_id() when user_id is None (or otherwise ensure a non-null value).

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +83
# Check get_database_schema has workflow guidance
assert "ALWAYS call this function FIRST" in source, \
"get_database_schema description should contain 'ALWAYS call this function FIRST'"

assert "before executing any SQL queries" in source, \
"get_database_schema description should guide calling before queries"

# Check get_table_list has workflow guidance
assert "Use this function first to discover which tables are available" in source, \
"get_table_list description should guide discovery workflow"

# Check get_table_schema has workflow guidance
assert "Call this after discovering tables" in source, \
"get_table_schema description should reference discovery step"

# Check get_relationships has workflow guidance
assert "JOIN conditions" in source, \
"get_relationships description should mention JOIN conditions"

print("✅ SQL Schema Plugin descriptions contain workflow guidance!")
return True

except Exception as e:
print(f"❌ Test failed: {e}")
import traceback
traceback.print_exc()
return False


def test_sql_query_plugin_descriptions():
"""Test that SQL Query Plugin has prescriptive workflow descriptions."""
print("🔍 Testing SQL Query Plugin @kernel_function descriptions...")

try:
from semantic_kernel_plugins.sql_query_plugin import SQLQueryPlugin
import inspect

source = inspect.getsource(SQLQueryPlugin)

# Check execute_query has schema requirement
assert "you MUST first call get_database_schema or get_table_list" in source, \
"execute_query description should require schema discovery first"

assert "fully qualified table names" in source, \
"execute_query description should mention fully qualified table names"

# Check execute_scalar has schema requirement
assert "MUST first discover the database schema" in source, \
"execute_scalar description should require schema discovery"

# Check validate_query has useful guidance
assert "pre-check complex queries" in source, \
"validate_query description should guide pre-checking"
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test asserts specific strings (e.g., "ALWAYS call this function FIRST", "you MUST first call...") that no longer match the updated plugin descriptions (which now use the resilient "If the database schema is provided in your instructions..." wording). As written, the test will fail even when the behavior is correct. Update the assertions to match the current descriptions and/or assert on higher-level invariants (e.g., presence of the resilient conditional guidance) to reduce brittleness.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants