Free cookie consent management tool by TermsFeed

Connect Document Viewer to an AI Chat Response

On This Page

Tip:  Interface patterns give you an opportunity to explore different interface designs. Be sure to check out How to Adapt a Pattern for Your Application.

Goal

This pattern illustrates how to connect a!documentViewerField component to an AI chat response.

This design pattern is not recommended for offline interfaces because reflecting immediate changes in an interface based on user interaction requires a connection to the server.

When you use an AI chat component to answer questions about a document, you can enhance the user experience by connecting the chat's sources directly to a document viewer. This pattern allows users to click a source citation in the chat response and instantly see the exact page and text the AI used for its answer, providing clear verification and context.

This example shows an interface with an AI chat component that includes a file upload and a document viewer component. When you ask a question, the AI's response is shown in the chat, and the document viewer on the right automatically highlights the source text for that answer.

How it works

This interface uses several local variables within a!localVariables to manage the state of the chat and document. The logic automatically calls an AI model, parses its response, and uses parts of that response to update the document viewer.

Here is the step-by-step flow:

  1. Upload Document: You upload a file using the a!fileUploadField, which saves the file into the local!document variable.

  2. Send Message: When you send a message, the saveInto parameter of the a!chatField appends your new message (as a "USER" type) to the local!messages variable.

  3. Call the AI:

    • The change in local!messages causes the local!llmResponse variable to recalculate.
    • This variable first checks that the last message was from a "USER".
    • It uses a custom function (getpdftext) to extract text from the local!document.
    • It then calls the a!callLanguageModel function. The prompt instructs the AI to return a JSON object containing an "answer" and a "source" quote.
  4. Parse the Response:

    • The a!fromJson() function parses the text response from the AI into a structured map.
    • The local!messagesWithLLMResponse variable appends the AI's "answer" to the chat history, which displays it in the a!chatField.
    • The local!textToHighlight variable is set using the "source" value from the AI's JSON response.
  5. Update Document Viewer: The a!documentViewerField is configured to use local!document for its document parameter and local!textToHighlight for its highlightText parameter. When local!textToHighlight is updated, the document viewer automatically re-evaluates and highlights the new text.

Example expression

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
a!localVariables(
  local!messages: {},
  local!document,
  local!documentContents: fn!if(
    fn!and(a!isNotNullOrEmpty(local!document), a!isNotNullOrEmpty(local!messages)),
    getpdftext(local!document[1]),
    ""
  ),
  local!llmResponse: fn!if(
    fn!count(local!messages) = 0,
    "",

    local!messages[fn!count(local!messages)].type = "USER",
    a!fromJson(a!callLanguageModel(
      fn!joinarray(
        a!forEach(
          items: local!messages,
          expression: fn!if(
            fv!index = 1,
            local!documentContents & char(10) & "Provide a response in the format {""answer"": ..., ""source"": <direct quote>}. Start your response with { and the response should be valid json. For the source give a short 5 word or less quote" & char(10) & fv!item.content,
            fv!item.content
          )
        ),
        "\n"
      ) & "{"
    )),

    ""
  ),
  local!messagesWithLLMResponse: fn!if(
    a!isNotNullOrEmpty(local!llmResponse),
    fn!append(
      local!messages,
      a!map(
        type: "LLM",
        content: local!llmResponse["answer"]
      )
    ),
    local!messages
  ),
  local!textToHighlight: fn!if(
    a!isNotNullOrEmpty(local!llmResponse),
    local!llmResponse["source"],
    ""
  ),
  a!columnsLayout(
    columns: {
      a!columnLayout(
        contents: a!chatField(
          title: a!sideBySideLayout(
            items: {
              a!sideBySideItem(
                item: a!textField(
                  value: "Chat with a Document",
                  readOnly: fn!true()
                )
              ),
              a!sideBySideItem(
                item: a!fileUploadField(
                  target: cons!DocUploads,
                  value: local!document,
                  saveInto: local!document
                )
              )
            }
          ),

          messages: a!forEach(
            items: local!messagesWithLLMResponse,
            expression: fv!item.content
          ),
          saveInto: {
            a!submitUploadedFiles(),
            a!save(
              local!messages,
              fn!append(
                local!messagesWithLLMResponse,
                a!map(type: "USER", content: save!value)
              )
            )
          }
        )
      ),
      a!columnLayout(
        contents: a!documentViewerField(
          document: local!document,
          highlightText: local!textToHighlight
        )
      )
    }
  )
  
)

Feedback