Chat API

This API is experimental and there may be breaking changes as it evolves.

Overview

The Glean Chat API provides a programmatic interface to interact with the Glean chat system. It allows developers to send and receive messages and handle conversation streams.

The Glean Chat API utilizes a streaming POST endpoint at https://<your-domain>-be.glean.com/rest/api/v1/chat for continuous, real-time conversational interactions. Remember to replace <your-domain-be> with your specific domain.

Setup

First, a Glean admin at https://app.glean.com/admin/setup/gleanassistant must set a key with access to GPT-4 as well as who has access to the Glean Assistant.

Authentication

Glean’s Chat API uses the standard HTTP Authorization header to transmit the bearer token, similar to Glean’s Client REST API authentication methods.

‘CHAT’ scoped bearer tokens are required to interact with Glean’s Chat API and can be generated from the admin console. More details about token permissions, scopes and generation can be found here.

Please note that all samples in the document assume the usage of a user-permissioned token. For a global-permissioned token, you would need to add an additional ‘X-Scio-Actas’ HTTP header to make the request on behalf of another user in your company. You can learn about making your first request here.

Personalisation & Privacy

Glean Search and Glean Chat ensure personalized search results based on your document access permissions. Be aware, if a user-permissioned token is used, the results might reflect your private information. Similarly, with a global token, the response may include private data of the user specified in the X-Scio-Actas HTTP header. This feature is designed to enhance data relevance, but it's important to be mindful of the privacy implications. Handle your tokens with care to maintain your personal information's confidentiality.

Schema & Payload

Up-to-date details about API schemas are available at https://developers.glean.com/client/operation/chat/. Please refer to the curl requests and python scripts below for sample usage.

Sample Code and Diagrams

Example Conversational Flow Diagram

The following diagram illustrates a multi-message conversation flow between an end user, a custom client, and the Glean server.

End userCustom clientGlean serverInputs messageSends POST request to /chat endpoint with messageReturns response with message streamDisplays response messageInputs another messageSends POST request to /chat endpoint with new message and previous responseReturns response with new message streamDisplays new response messageEnd userCustom clientGlean server

Sample cURL request

Using a user-permissioned token (non-streaming response)

Copy
Copied
$ curl 'https://<your-domain>-be.glean.com/rest/api/v1/chat' \
  -H 'content-type: text/plain;charset=UTF-8' \
  -H 'Authorization: Bearer <TOKEN>' \
  --data-raw '{
    "stream": false,
    "messages": [
        {
            "author": "USER",
            "fragments": [
                {
                    "text": "What are the holidays this year?"
                }
            ]
        }
    ]
}' 
# Note: jq can be used for easier visualisation of the json response

Using a global-permissioned token (non-streaming response)

Copy
Copied
$ curl 'https://<your-domain>-be.glean.com/rest/api/v1/chat' \
  -H 'Authorization: Bearer <TOKEN>' \
  -H 'X-Scio-Actas: john.doe@yourcompany.com' \
  --data-raw '{
    "stream": false,
    "messages": [
      {
        "author": "USER",
        "fragments": [
          {
            "text": "What are the holidays this year?"
          }
        ]
      }
    ]
  }' \
  --compressed
# Note: jq can be used for easier visualisation of the json response

Sample python scripts to interact with the API

Variant 1: Single user message (can choose between streaming vs non-streaming)

Note: The following example streams the output as it becomes available, whenever possible. To turn off streaming outputs, please set the stream field in the request body to False

Copy
Copied
import requests
import json


def process_message_fragment(message):
    message_type = message['messageType']
    fragments = message.get('fragments', [])
    citations = message.get('citations', [])

    message_fragment_str = ''
    if message_type == 'CONTENT':
        if fragments:
            for fragment in fragments:
                text = fragment.get('text', '')
                print(text, end='', flush=True)
        if citations:
            print('\nSources:')
            for idx, citation in enumerate(citations):
                sourceDocument = citation.get('sourceDocument', {})
                if sourceDocument:
                  source = citation['sourceDocument']
                  print(f'Source {idx + 1}: Document title - {source["title"]}, url: {source["url"]}')
                sourcePerson = citation.get('sourcePerson', {})
                if sourcePerson:
                  source = citation['sourcePerson']
                  print(f'Source {idx + 1}: Person name - {source["name"]}')



def process_response_message_stream(response):
    for line in response.iter_lines():
        if line:
            line_json = json.loads(line)
            messages = line_json.get('messages', [])
            for message in messages:
                process_message_fragment(message)

def main():
    url = 'https://<your-domain>-be.glean.com/rest/api/v1/chat'
    headers = {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer <TOKEN>'
    }
    data = {
        'stream': True, # Set to False to toggle off streaming mode
        'messages': [{
            'author': 'USER',
            'fragments': [{'text': 'What are the holidays this year?'}]
        }],
    }

    try:
        with requests.post(url, headers=headers, json=data, stream=True) as response:
            if response.status_code == 200:
                process_response_message_stream(response)
            else:
                print(f'Status code: {response.status_code}, error: {response.text}')
                exit(1)
    except requests.exceptions.RequestException as e:
        print(f'Request Exception: {str(e)}')
        exit(1)


if __name__ == '__main__':
    main()

Variant 2: Multiple message conversations (can choose between streaming vs non-streaming)

Note: The following example streams the output as it becomes available, whenever possible. To turn off streaming outputs, please set the stream field in the request body to False

Copy
Copied
import requests
import json
from typing import List, Dict


def process_message_fragment(message):
    message_fragment_str = ''
    message_fragment_citations = []

    message_type = message['messageType']
    fragments = message.get('fragments', [])
    citations = message.get('citations', [])

    message_fragment_str = ''
    if message_type == 'CONTENT':
        if fragments:
            for fragment in fragments:
                text = fragment.get('text', '')
                print(text, end='', flush=True)
                message_fragment_str += text
        if citations:
            print('\nSources:')
            message_fragment_citations += citations
            for idx, citation in enumerate(citations):
                sourceDocument = citation.get('sourceDocument', {})
                if sourceDocument:
                  source = citation['sourceDocument']
                  print(f'Source {idx + 1}: Document title - {source["title"]}, url: {source["url"]}')
                sourcePerson = citation.get('sourcePerson', {})
                if sourcePerson:
                  source = citation['sourcePerson']
                  print(f'Source {idx + 1}: Person name - {source["name"]}')


    return message_fragment_str, message_fragment_citations


def make_content_message(author: str = 'USER', text: str = None, citations: List[Dict] = None):
    # Create a content message JSON object
    message_json = {
        'author': author,
        'messageType': 'CONTENT'
    }
    if text:
        message_json['fragments'] = [{'text': text}]
    if citations:
        message_json['citations'] = citations
    return message_json


def process_response_message_stream(response):
    response_message_text = ''
    response_message_citations = []
    chat_session_tracking_token = None

    for line in response.iter_lines():
        if line:
            line_json = json.loads(line)
            messages = line_json.get('messages', [])
            chat_session_tracking_token = line_json.get('chatSessionTrackingToken', None)
            for message_fragment in messages:
                message_fragment_text, message_fragment_citations = process_message_fragment(message_fragment)
                response_message_text += message_fragment_text
                response_message_citations += message_fragment_citations

    return make_content_message(author='GLEAN_AI', text=response_message_text, citations=response_message_citations), chat_session_tracking_token


def send_conversation_message(url, headers, payload):
    next_payload = payload
    try:
        with requests.post(url, headers=headers, json=payload, stream=True) as response:
            if response.status_code == 200:
                response_message, chat_session_tracking_token = process_response_message_stream(response)
                # Add the response message to the next payload, most recent message first
                next_payload['messages'].insert(0, response_message)
                next_payload['chatSessionTrackingToken'] = chat_session_tracking_token
            else:
                print(f'Status code: {response.status_code}, error: {response.text}')
                exit(1)
    except requests.exceptions.RequestException as e:
        print(f'Request Exception: {str(e)}')
        exit(1)
    return next_payload


def main():
    url = 'https://<your-domain>-be.glean.com/rest/api/v1/chat'
    headers = {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer <TOKEN>'
    }

    # Initialize the payload
    payload = {
        'stream': True,  # Set to False to toggle off streaming mode
        'messages': []
    }

    first_user_message = make_content_message(text='What are the holidays this year?')
    second_user_message = make_content_message(text='What about this month?')

    user_messages_list = [first_user_message, second_user_message]

    for user_message in user_messages_list:
        print(f'User message: {user_message["fragments"][0]["text"]}', flush=True)
        print('Response: ', flush=True)
        payload['messages'].insert(0, user_message) # Add the user message to the payload, most recent message first
        payload = send_conversation_message(url, headers, payload)  # Send conversation message and get next payload


if __name__ == '__main__':
    main()