This is an automated email from the ASF dual-hosted git repository.

voidmatcha pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zeppelin.git


The following commit(s) were added to refs/heads/master by this push:
     new 2d14bb99bd [ZEPPELIN-6759] Add get_paragraph tool to the AI assistant
2d14bb99bd is described below

commit 2d14bb99bdf2091b4ff36edd5a623e12a93ead1c
Author: SeungYoung Oh <[email protected]>
AuthorDate: Mon Oct 26 21:55:34 2026 +0900

    [ZEPPELIN-6759] Add get_paragraph tool to the AI assistant
    
    ### What is this PR for?
    Add a get_paragraph assistant tool to retrieve a paragraph’s code, 
execution status, and output.
    
    ### What type of PR is it?
    Feature
    
    ### Todos
    
    ### What is the Jira issue?
    [[ZEPPELIN-6759]](https://issues.apache.org/jira/browse/ZEPPELIN-6759)
    
    ### How should this be tested?
    Run GetParagraphToolTest, ListParagraphsToolTest, and AssistantTest.
    
    ### Screenshots (if appropriate)
    
    ### Questions:
    * Does the license files need to update? N
    * Is there breaking changes for older versions? N
    * Does this needs documentation? N
    
    
    Closes #5586 from seung-00/feature/ZEPPELIN-6759.
    
    Signed-off-by: YONGJAE LEE <[email protected]>
---
 .../zeppelin/service/assistant/AssistantImpl.java  |  3 +-
 ...stParagraphsTool.java => GetParagraphTool.java} | 65 +++++++++++++---------
 .../service/assistant/ListParagraphsTool.java      | 24 +++-----
 ...aphsToolTest.java => GetParagraphToolTest.java} | 53 +++++++++---------
 .../service/assistant/ListParagraphsToolTest.java  |  5 +-
 5 files changed, 78 insertions(+), 72 deletions(-)

diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/service/assistant/AssistantImpl.java
 
b/zeppelin-server/src/main/java/org/apache/zeppelin/service/assistant/AssistantImpl.java
index 9020c36625..fd405311ae 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/service/assistant/AssistantImpl.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/service/assistant/AssistantImpl.java
@@ -82,7 +82,8 @@ public class AssistantImpl implements Assistant {
     this.conversationRepository = conversationRepository;
     this.toolExecutor = new ToolExecutor(
         List.of(
-            new ListParagraphsTool(notebookService)
+            new ListParagraphsTool(notebookService),
+            new GetParagraphTool(notebookService)
         )
     );
   }
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/service/assistant/ListParagraphsTool.java
 
b/zeppelin-server/src/main/java/org/apache/zeppelin/service/assistant/GetParagraphTool.java
similarity index 52%
copy from 
zeppelin-server/src/main/java/org/apache/zeppelin/service/assistant/ListParagraphsTool.java
copy to 
zeppelin-server/src/main/java/org/apache/zeppelin/service/assistant/GetParagraphTool.java
index 9104ae45f3..8ac66c9cbf 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/service/assistant/ListParagraphsTool.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/service/assistant/GetParagraphTool.java
@@ -18,67 +18,80 @@
 package org.apache.zeppelin.service.assistant;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.zeppelin.user.AuthenticationInfo;
 
-import org.apache.zeppelin.notebook.Paragraph;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.zeppelin.notebook.Note;
+import org.apache.zeppelin.rest.exception.ParagraphNotFoundException;
 import org.apache.zeppelin.service.NotebookService;
 import org.apache.zeppelin.service.ServiceContext;
 import org.apache.zeppelin.service.SimpleServiceCallback;
+import org.apache.zeppelin.user.AuthenticationInfo;
 
-public class ListParagraphsTool implements Tool {
+public class GetParagraphTool implements Tool {
+
+  private static final int MAX_RESULT_LENGTH = 10_000;
 
   private final NotebookService notebookService;
 
-  public ListParagraphsTool(NotebookService notebookService) {
+  public GetParagraphTool(NotebookService notebookService) {
     this.notebookService = notebookService;
   }
 
   @Override
   public String name() {
-    return "list_paragraphs";
+    return "get_paragraph";
   }
 
   @Override
   public String description() {
-    return "List notebook paragraphs.";
+    return "Get a paragraph's input, status, and latest result (truncated at 
10000 chars).";
   }
 
   @Override
   public Map<String, Object> parameters() {
-    return Map.of("type", "object", "properties", Map.of());
+    return Map.of(
+        "type", "object",
+        "properties", Map.of("paragraphId", Map.of("type", "string")),
+        "required", List.of("paragraphId")
+    );
   }
 
   @Override
-  public List<Map<String, Object>> call(
+  public Map<String, Object> call(
       String noteId,
       Map<String, Object> args,
       AuthenticationInfo authInfo,
       Set<String> userAndRoles
   ) throws IOException {
-    ServiceContext context = new ServiceContext(authInfo, userAndRoles);
-    return notebookService.getNote(noteId, context, callback(), note -> {
-      List<Map<String, Object>> result = new ArrayList<>();
-      for (int i = 0; i < note.getParagraphCount(); i++) {
-        Paragraph p = note.getParagraph(i);
-        Map<String, Object> info = new HashMap<>();
-        info.put("id", p.getId());
-        info.put("title", p.getTitle());
-        info.put("text", StringUtils.abbreviate(p.getText(), 500));
-        info.put("index", i);
-        result.add(info);
-      }
-      return result;
+    Object value = args.get("paragraphId");
+    if (!(value instanceof String) || ((String) value).isBlank()) {
+      throw new IllegalArgumentException("paragraphId must be a non-blank 
string");
+    }
+    var paragraphId = (String) value;
+    var context = new ServiceContext(authInfo, userAndRoles);
+
+    return notebookService.getNote(noteId, context, throwOnFailure(), note -> {
+      var paragraph = Optional.ofNullable(note.getParagraph(paragraphId))
+          .orElseThrow(() -> new ParagraphNotFoundException(paragraphId));
+
+      return Map.of(
+          "id", paragraph.getId(),
+          "title", StringUtils.defaultString(paragraph.getTitle()),
+          "input", StringUtils.defaultString(paragraph.getText()),
+          "status", paragraph.getStatus().name(),
+          "result", Optional.ofNullable(paragraph.getReturn())
+              .map(r -> StringUtils.abbreviate(r.toString(), 
MAX_RESULT_LENGTH))
+              .orElse("")
+      );
     });
   }
 
-  private static <T> SimpleServiceCallback<T> callback() {
-    return new SimpleServiceCallback<T>() {
+  private static SimpleServiceCallback<Note> throwOnFailure() {
+    return new SimpleServiceCallback<Note>() {
       @Override
       public void onFailure(Exception ex, ServiceContext context) throws 
IOException {
         if (ex instanceof RuntimeException) {
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/service/assistant/ListParagraphsTool.java
 
b/zeppelin-server/src/main/java/org/apache/zeppelin/service/assistant/ListParagraphsTool.java
index 9104ae45f3..a3bdde7d3c 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/service/assistant/ListParagraphsTool.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/service/assistant/ListParagraphsTool.java
@@ -18,15 +18,13 @@
 package org.apache.zeppelin.service.assistant;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.zeppelin.user.AuthenticationInfo;
 
-import org.apache.zeppelin.notebook.Paragraph;
 import org.apache.zeppelin.service.NotebookService;
 import org.apache.zeppelin.service.ServiceContext;
 import org.apache.zeppelin.service.SimpleServiceCallback;
@@ -62,19 +60,13 @@ public class ListParagraphsTool implements Tool {
       Set<String> userAndRoles
   ) throws IOException {
     ServiceContext context = new ServiceContext(authInfo, userAndRoles);
-    return notebookService.getNote(noteId, context, callback(), note -> {
-      List<Map<String, Object>> result = new ArrayList<>();
-      for (int i = 0; i < note.getParagraphCount(); i++) {
-        Paragraph p = note.getParagraph(i);
-        Map<String, Object> info = new HashMap<>();
-        info.put("id", p.getId());
-        info.put("title", p.getTitle());
-        info.put("text", StringUtils.abbreviate(p.getText(), 500));
-        info.put("index", i);
-        result.add(info);
-      }
-      return result;
-    });
+    return notebookService.getNote(noteId, context, callback(), note ->
+        note.getParagraphs().stream()
+            .map(p -> Map.<String, Object>of(
+                "id", p.getId(),
+                "title", StringUtils.defaultString(p.getTitle()),
+                "input", 
StringUtils.abbreviate(StringUtils.defaultString(p.getText()), 500)))
+            .collect(Collectors.toList()));
   }
 
   private static <T> SimpleServiceCallback<T> callback() {
diff --git 
a/zeppelin-server/src/test/java/org/apache/zeppelin/service/assistant/ListParagraphsToolTest.java
 
b/zeppelin-server/src/test/java/org/apache/zeppelin/service/assistant/GetParagraphToolTest.java
similarity index 61%
copy from 
zeppelin-server/src/test/java/org/apache/zeppelin/service/assistant/ListParagraphsToolTest.java
copy to 
zeppelin-server/src/test/java/org/apache/zeppelin/service/assistant/GetParagraphToolTest.java
index 4cdab27b63..35887dc32a 100644
--- 
a/zeppelin-server/src/test/java/org/apache/zeppelin/service/assistant/ListParagraphsToolTest.java
+++ 
b/zeppelin-server/src/test/java/org/apache/zeppelin/service/assistant/GetParagraphToolTest.java
@@ -23,22 +23,25 @@ import static org.mockito.Mockito.*;
 
 import java.util.Map;
 import java.util.Set;
+
 import org.apache.zeppelin.interpreter.InterpreterFactory;
+import org.apache.zeppelin.interpreter.InterpreterResult;
 import org.apache.zeppelin.notebook.AuthorizationService;
 import org.apache.zeppelin.notebook.Note;
 import org.apache.zeppelin.notebook.Notebook;
 import org.apache.zeppelin.notebook.Notebook.NoteProcessor;
 import org.apache.zeppelin.rest.exception.ForbiddenException;
+import org.apache.zeppelin.scheduler.Job.Status;
 import org.apache.zeppelin.service.NotebookService;
 import org.apache.zeppelin.user.AuthenticationInfo;
 import org.junit.jupiter.api.Test;
 
-class ListParagraphsToolTest {
+class GetParagraphToolTest {
   private final AuthenticationInfo authInfo = AuthenticationInfo.ANONYMOUS;
   private final Set<String> userAndRoles = Set.of("user");
 
   @Test
-  void listsParagraphs() throws Exception {
+  void getsParagraph() throws Exception {
     var note = new Note();
     note.setInterpreterFactory(mock(InterpreterFactory.class));
     var notebook = mock(Notebook.class);
@@ -46,42 +49,40 @@ class ListParagraphsToolTest {
         ((NoteProcessor<?>) invocation.getArgument(2)).process(note));
     var authorization = mock(AuthorizationService.class);
     when(authorization.isReader("noteId", userAndRoles)).thenReturn(true);
-    var notebookService = new NotebookService(notebook, authorization, null, 
null);
-    var sut = new ListParagraphsTool(notebookService);
+    var sut = new GetParagraphTool(new NotebookService(notebook, 
authorization, null, null));
 
-    var shortParagraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
-    shortParagraph.setText("%md hello");
-    var longParagraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
-    var longText = "%python\n" + "x".repeat(500);
-    longParagraph.setText(longText);
+    var paragraph = note.addNewParagraph(authInfo);
+    var text = "%python\n" + "x".repeat(600);
+    paragraph.setText(text);
+    paragraph.setTitle("Example");
+    paragraph.setStatus(Status.FINISHED);
+    paragraph.setResult(new InterpreterResult(
+        InterpreterResult.Code.SUCCESS, InterpreterResult.Type.TEXT, 
"x".repeat(20_000)));
 
-    var paragraphs = sut.call("noteId", Map.of(), authInfo, userAndRoles);
-    assertEquals(2, paragraphs.size());
-    assertEquals(shortParagraph.getId(), paragraphs.get(0).get("id"));
-    assertEquals(longParagraph.getId(), paragraphs.get(1).get("id"));
-    assertEquals("%md hello", paragraphs.get(0).get("text"));
-    assertEquals(longText.substring(0, 497) + "...", 
paragraphs.get(1).get("text"));
-    assertFalse(paragraphs.get(1).containsKey("interpreter"));
-    assertEquals(longText, longParagraph.getText());
-    assertEquals(1, paragraphs.get(1).get("index"));
+    var actual = sut.call(
+        "noteId",
+        Map.of("paragraphId", paragraph.getId()),
+        authInfo,
+        userAndRoles
+    );
+    assertEquals(paragraph.getId(), actual.get("id"));
+    assertEquals("Example", actual.get("title"));
+    assertEquals(text, actual.get("input"));
+    assertEquals("FINISHED", actual.get("status"));
   }
 
   @Test
   void rejectsNonReaders() throws Exception {
     var notebook = mock(Notebook.class);
-    when(notebook.processNote(eq("noteId"), eq(false), any()))
-        .thenAnswer(invocation -> ((NoteProcessor<?>) 
invocation.getArgument(2))
-            .process(new Note()));
+    when(notebook.processNote(eq("noteId"), eq(false), 
any())).thenAnswer(invocation ->
+        ((NoteProcessor<?>) invocation.getArgument(2)).process(new Note()));
     var authorization = mock(AuthorizationService.class);
     when(authorization.isReader("noteId", userAndRoles)).thenReturn(false);
-
-    var notebookService = new NotebookService(notebook, authorization, null, 
null);
-
-    var sut = new ListParagraphsTool(notebookService);
+    var sut = new GetParagraphTool(new NotebookService(notebook, 
authorization, null, null));
 
     assertThrows(
         ForbiddenException.class,
-        () -> sut.call("noteId", Map.of(), authInfo, userAndRoles)
+        () -> sut.call("noteId", Map.of("paragraphId", "any"), authInfo, 
userAndRoles)
     );
   }
 }
diff --git 
a/zeppelin-server/src/test/java/org/apache/zeppelin/service/assistant/ListParagraphsToolTest.java
 
b/zeppelin-server/src/test/java/org/apache/zeppelin/service/assistant/ListParagraphsToolTest.java
index 4cdab27b63..a6a31ba893 100644
--- 
a/zeppelin-server/src/test/java/org/apache/zeppelin/service/assistant/ListParagraphsToolTest.java
+++ 
b/zeppelin-server/src/test/java/org/apache/zeppelin/service/assistant/ListParagraphsToolTest.java
@@ -59,11 +59,10 @@ class ListParagraphsToolTest {
     assertEquals(2, paragraphs.size());
     assertEquals(shortParagraph.getId(), paragraphs.get(0).get("id"));
     assertEquals(longParagraph.getId(), paragraphs.get(1).get("id"));
-    assertEquals("%md hello", paragraphs.get(0).get("text"));
-    assertEquals(longText.substring(0, 497) + "...", 
paragraphs.get(1).get("text"));
+    assertEquals("%md hello", paragraphs.get(0).get("input"));
+    assertEquals(longText.substring(0, 497) + "...", 
paragraphs.get(1).get("input"));
     assertFalse(paragraphs.get(1).containsKey("interpreter"));
     assertEquals(longText, longParagraph.getText());
-    assertEquals(1, paragraphs.get(1).get("index"));
   }
 
   @Test

Reply via email to