Moldable Chat: Creating chats and shaping their output

The simplest chat can be created like this:

GtLChat new
  

Inspecting the object reveals a view with an interactive chat interface. Go ahead and try it.

You can also control the chat programmatically. For example, to send an initial message, use GtLChat>>#sendMarkdown: sendMarkdown: aString | newMessage | self assert: aString isString. newMessage := self createUserMessage. newMessage markdown: aString. self sendMessage: newMessage :

c := GtLChat new.
c sendMarkdown: 'Write a script for adding 2 and 40 in Smalltalk'.
  

A basic chat has no notion of the expected output. With services that support structured output, we can define the expected response schema. For example, in the previous chat, we received a plain string, so it is shown as plain text even if it contains Markdown notation. If we specify that we expect Markdown, we can do so through GtLChat>>#markdownResponse markdownResponse self addResponseFormatForMagritte: GtLMarkdown named: 'Markdown' :

c := GtLChat new markdownResponse.
c sendMarkdown: 'Write a script for adding 2 and 40 in Smalltalk. And a short poem about it.'
  

Executing this chat now shows a Markdown-specific view in addition to the plain text view. This works as follows: the engine sends the expected response schema in the request, and when the response arrives, it transparently instantiates objects that conform to the specified schema. In our case, the object is an instance of GtLMarkdown Object << #GtLMarkdown slots: { #content }; tag: 'Formats'; package: 'Gt4LlmCore' , and that object knows how to present itself through a styled editor.

Structured output can be exploited for various purposes.

For example, suppose we want to preview proposed changes from the LLM. In that case, we can specify that we expect code transformations. To add a new response schema, we use GtLChat>>#addResponseFormatForMagrite:named: addResponseFormatForMagrite: aClass named: aString self deprecated: 'use addResponseFormatForMagritte:named:' transformWith: '`@rcv addResponseFormatForMagrite: `@arg1 named: `@arg2' -> '`@rcv addResponseFormatForMagritte: `@arg1 named: `@arg2'. ^ self addResponseFormatForMagritte: aClass named: aString. , which takes the schema from a Magritte class. In our case, the class is GtLCodeTransformations GtLBasicTransformations << #GtLCodeTransformations slots: {}; tag: 'Formats'; package: 'Gt4LlmCore' , which, besides describing the structure of different kinds of transformations, also knows how to turn those transformations into dedicated changes that can be applied directly in the image:

c := GtLChat new
		markdownResponse;
		addResponseFormatForMagrite: GtLCodeTransformations
			named: 'CodeTransformations';
		sendMarkdown: 'Create a class for a Person with a first and last name. Create a printing method. Use CodeTransformations to express the changes.'