Google Cloud Natural Language over gRPC in Delphi

· Components

Google Cloud Natural Language reads unstructured text and tells you what is in it. It measures the overall sentiment of a document, extracts the people, places and organizations it mentions as entities, and classifies the text into a content taxonomy. The sgcWebSockets Enterprise edition ships a typed Natural Language gRPC client on top of TsgcGRPCClient, so you can reach the language.googleapis.com service straight from Delphi and C++Builder without any external gRPC runtime.

How it works

The Natural Language API is exposed as a gRPC service, and gRPC is Protocol Buffers messages framed over HTTP/2. sgcWebSockets already ships a complete HTTP/2 stack, so the gRPC client sits on a TsgcHTTP2Client transport pointed at language.googleapis.com:443 over TLS. TsgcGRPCClient handles the framing, the content type and the status trailers; the typed unit sgcGRPC_Google_Language handles the request and response messages.

Each request wraps a document: the text to analyze plus its type (plain text or HTML) and an optional language hint. The typed classes serialize that document to the exact Protocol Buffers wire layout the API expects, and parse the reply back into ready-to-read properties. You never hand-assemble a protobuf or chase field numbers.

Authentication uses a Google Cloud service account. A self-signed service-account JWT is exchanged for a bearer token, which is then sent as gRPC metadata on every call. Because that token is audience-bound, the JWT is minted against the Natural Language endpoint so it is accepted by language.googleapis.com.

Setting up the client

Create an HTTP/2 transport for the Natural Language host, then attach a TsgcGRPCClient to it and select the binary protobuf content type. The bearer token obtained from the service account is added to DefaultMetadata as a standard authorization header, so it travels on every request.

uses
  sgcHTTP2_Client, sgcGRPC_Client, sgcGRPC_Types, sgcGRPC_Google_Language;

var
  HTTP2: TsgcHTTP2Client;
  GRPC: TsgcGRPCClient;
begin
  HTTP2 := TsgcHTTP2Client.Create(nil);
  HTTP2.Host := 'language.googleapis.com';
  HTTP2.Port := 443;
  HTTP2.TLS  := True;

  GRPC := TsgcGRPCClient.Create(nil);
  GRPC.Client := HTTP2;
  GRPC.ChannelOptions.ContentType := grpcProto;
  GRPC.ChannelOptions.Compression := grpcNoCompression;

  // bearer token from the service-account JWT exchange
  GRPC.DefaultMetadata.Add('authorization', 'Bearer ' + Token);

  HTTP2.Active := True;
end;

Analyzing sentiment

Sentiment analysis reports an emotional score from -1.0 (negative) to +1.0 (positive) and a magnitude that reflects how much emotional content the document carries. Build a TsgcGRPCLanguageAnalyzeSentimentRequest, set the document content, call the AnalyzeSentiment method on the LanguageService, then load the reply bytes into a typed response.

var
  oRequest: TsgcGRPCLanguageAnalyzeSentimentRequest;
  oResponse: TsgcGRPCResponse;
  oResult: TsgcGRPCLanguageAnalyzeSentimentResponse;
begin
  oRequest := TsgcGRPCLanguageAnalyzeSentimentRequest.Create;
  try
    oRequest.Document.DocType := 1; // PLAIN_TEXT
    oRequest.Document.Content := 'sgcWebSockets makes gRPC in Delphi easy.';

    oResponse := GRPC.Call('google.cloud.language.v1.LanguageService',
      'AnalyzeSentiment', oRequest.ToBytes);

    if oResponse.StatusCode = grpcOK then
    begin
      oResult := TsgcGRPCLanguageAnalyzeSentimentResponse.Create;
      try
        oResult.LoadFromBytes(oResponse.Data);
        ShowMessage('Score: ' + FloatToStr(oResult.DocumentSentiment.Score) +
          '  Magnitude: ' + FloatToStr(oResult.DocumentSentiment.Magnitude));
      finally
        oResult.Free;
      end;
    end;
  finally
    oRequest.Free;
  end;
end;

Extracting entities

Entity analysis returns the salient things a document talks about. The response is a list of entities, each with a Name, an EntityType (person, location, organization and so on) and a Salience score that ranks how central the entity is to the text. The typed response gives you an indexed list to walk.

var
  oRequest: TsgcGRPCLanguageAnalyzeEntitiesRequest;
  oResponse: TsgcGRPCResponse;
  oResult: TsgcGRPCLanguageAnalyzeEntitiesResponse;
  i: Integer;
begin
  oRequest := TsgcGRPCLanguageAnalyzeEntitiesRequest.Create;
  try
    oRequest.Document.DocType := 1; // PLAIN_TEXT
    oRequest.Document.Content := Memo1.Text;

    oResponse := GRPC.Call('google.cloud.language.v1.LanguageService',
      'AnalyzeEntities', oRequest.ToBytes);

    oResult := TsgcGRPCLanguageAnalyzeEntitiesResponse.Create;
    try
      oResult.LoadFromBytes(oResponse.Data);
      for i := 0 to oResult.EntityCount - 1 do
        Memo2.Lines.Add(oResult.Entity(i).Name + ' (type ' +
          IntToStr(oResult.Entity(i).EntityType) + ', salience ' +
          FloatToStr(oResult.Entity(i).Salience) + ')');
    finally
      oResult.Free;
    end;
  finally
    oRequest.Free;
  end;
end;

Classifying text

Content classification sorts a document into Google's category taxonomy, returning a list of categories each with a Name such as /Computers & Electronics/Software and a Confidence value. The pattern is the same: build a TsgcGRPCLanguageClassifyTextRequest, call ClassifyText, then read the categories from TsgcGRPCLanguageClassifyTextResponse with CategoryCount and Category(i). Classification needs a reasonable amount of text to work well, so feed it more than a single sentence.

Availability

The Natural Language gRPC client is part of the sgcWebSockets Enterprise edition and runs on Windows, macOS, Linux, iOS and Android, on Delphi and C++Builder. A complete, ready-to-run sample is in Demos\21.GRPC\14.Natural_Language, and the underlying client is documented on the gRPC Client product page.

Questions or feedback? Get in touch. You will get a reply from the people who wrote the code.