diff --git a/core/src/org/labkey/core/mpc/McpServiceImpl.java b/core/src/org/labkey/core/mpc/McpServiceImpl.java index 6aa6f9d7d56..422d0140223 100644 --- a/core/src/org/labkey/core/mpc/McpServiceImpl.java +++ b/core/src/org/labkey/core/mpc/McpServiceImpl.java @@ -65,7 +65,9 @@ import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; +import java.util.ConcurrentModificationException; import java.util.List; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.function.Supplier; @@ -314,6 +316,7 @@ public ChatClient getChat(HttpSession session, String agentName, Supplier sendMessageEx(ChatClient chatSession, String messag { if (isBlank(message)) return List.of(); - var callResponse = chatSession - .prompt(message) - .toolContext(McpContext.get().getToolContext().getContext()) - .call(); - List ret = new ArrayList<>(); - for (Generation result : callResponse.chatResponse().getResults()) + try { - var output = result.getOutput(); - if (ASSISTANT == output.getMessageType()) + var callResponse = chatSession + .prompt(message) + .toolContext(McpContext.get().getToolContext().getContext()) + .call(); + List ret = new ArrayList<>(); + for (Generation result : callResponse.chatResponse().getResults()) { - String md = output.getText(); - HtmlString html = HtmlString.unsafe(MarkdownService.get().toHtml(md)); - ret.add(new MessageResponse("text/markdown", md, html)); + var output = result.getOutput(); + if (ASSISTANT == output.getMessageType()) + { + String md = output.getText(); + HtmlString html = HtmlString.unsafe(MarkdownService.get().toHtml(md)); + ret.add(new MessageResponse("text/markdown", md, html)); + } } + return ret; + } + catch (NoSuchElementException x) + { + // Spring AI GoogleGenAiChatModel bug: empty candidates cause NoSuchElementException + // https://github.com/spring-projects/spring-ai/issues/4556 + LOG.warn("Empty response from chat model (likely a filtered or empty candidate)", x); + return List.of(new MessageResponse("text/plain", "The model returned an empty response. Please try rephrasing your question.", HtmlString.of("The model returned an empty response. Please try rephrasing your question."))); + } + catch (ConcurrentModificationException x) + { + // This can happen when the vector store is still loading, typically a problem shortly after startup + // Should do better synchronization or state checking + LOG.warn("Vector store not ready", x); + return List.of(new MessageResponse("text/plain", "Vector store likely not ready yet. Try again.", HtmlString.of("Vector store likely not ready yet. Try again."))); } - return ret; } @@ -464,6 +494,7 @@ public String getModel() // gemini-2.5-flash // gemini-2.5-pro // gemini-3-flash-preview + // gemini-3-pro-preview } @Override diff --git a/query/src/org/labkey/query/controllers/LabKeySql.md b/query/src/org/labkey/query/controllers/LabKeySql.md index 9beef68e462..82f4e45ca78 100644 --- a/query/src/org/labkey/query/controllers/LabKeySql.md +++ b/query/src/org/labkey/query/controllers/LabKeySql.md @@ -57,16 +57,16 @@ A `PIVOT` query helps you summarize and re-visualize data by transforming rows i PIVOT new_column_name BY pivoting_column IN ('value1', 'value2') ``` Note that pivot column names are case-sensitive. You may need to use `LOWER()` or `UPPER()` in your query to work around this issue. -* **Pivoting by Two Columns:** - Two levels of `PIVOT` are not directly supported. However, you can achieve a similar result by concatenating the two values together and pivoting on that "calculated" column. - ```sql - SELECT - Run.SampleCondition || ' ' || PeakLabel AS ConditionPeak, - AVG(Data.PercTimeCorrArea) AS AvgPercTimeCorrArea - FROM Data - GROUP BY Run.SampleCondition || ' ' || PeakLabel - PIVOT AvgPercTimeCorrArea BY ConditionPeak - ``` + * **Pivoting by Two Columns:** + Two levels of `PIVOT` are not directly supported. However, you can achieve a similar result by concatenating the two values together and pivoting on that "calculated" column. + ```sql + SELECT + Run.SampleCondition || ' ' || PeakLabel AS ConditionPeak, + AVG(Data.PercTimeCorrArea) AS AvgPercTimeCorrArea + FROM Data + GROUP BY Run.SampleCondition || ' ' || PeakLabel + PIVOT AvgPercTimeCorrArea BY ConditionPeak + ``` ----- @@ -130,7 +130,26 @@ LabKey SQL allows you to directly annotate your SQL statements to override how c ----- -### **7. Available Methods** +### **7. Container Filters** + +In addition to targeting a container by its path, LabKey SQL supports container filters to alter the scope +of a query. Annotate tables in the FROM clause with an optional container filter. Syntax: + +SELECT * FROM Issues [ContainerFilter='CurrentAndSubfolders'] alias + +Possible values include: +- AllFolders +- AllInProject +- AllInProjectPlusShared +- Current +- CurrentAndFirstChildren +- CurrentAndParents +- CurrentAndSubfolders +- CurrentAndSubfoldersPlusShared +- CurrentPlusProject +- CurrentPlusProjectAndShared. + +### **8. Available Methods** Here is a summary of the available functions and methods in LabKey SQL.