LLM tools by example

A tool can be used by an LLM chat to execute logic. For example, the below chat looks up the source of multiple methods using GtLMagritteToolForMethodsSource GtLAbstractMagritteFunctionTool << #GtLMagritteToolForMethodsSource slots: {}; tag: 'Tools'; package: 'Gt4LlmCore' .

methodSourcesForExistingMethods
	<gtExample>
	| tool call result model |
	tool := GtLMagritteToolForMethodsSource new.
	call := GtLFunctionToolCall new.
	call rawArguments: {
		'methodDescriptors' -> {
			{'methodClassName' -> 'Collection'.
				'methodName' -> 'withAll:'.
				'classSide' -> true} asDictionary.
			{'methodClassName' -> #GtLMagritteToolForMethodsSourceExamples.
				'methodName' -> 'methodSourcesForExistingMethods'.
				'classSide' -> false} asDictionary
		} asArray
	} asDictionary.
	result := tool performToolCall: call.
	self assert: call tool == tool.
	self assert: call arguments size equals: 1.
	self assert: (call arguments anyOne 
		allSatisfy: [:e | e isKindOf: GtLMethodReference]).
		
	self assert: (result isKindOf: GtLMagritteBasedModel).
	model := result model.
	self assert: model methods size equals: 2.
	self
		assert: model methods first sourceCode
		equals: (Collection class>>#withAll:) sourceCode.
	self
		assert: model methods second sourceCode
		equals: (GtLMagritteToolForMethodsSourceExamples>>#methodSourcesForExistingMethods) sourceCode.
	^ call
    

A tool's implementation has a name, takes arguments and returns a result.

GtLMagritteToolForMethodsSource GtLAbstractMagritteFunctionTool << #GtLMagritteToolForMethodsSource slots: {}; tag: 'Tools'; package: 'Gt4LlmCore' is a typical tool and can serve as blueprint for most tools: - It relies on Magritte descriptions to define the arguments. A sample is at GtLMagritteToolForMethodsSource>>#methodDescriptorsDescription methodDescriptorsDescription <magritteDescription> ^ MAToManyRelationDescription new accessor: #methodDescriptors; classes: {GtLMethodReference}; beRequired . - It returns a result object that is also annotated with Magritte descriptions. A sample is at GtLMethodsWithSource Object << #GtLMethodsWithSource slots: { #methods }; tag: 'Magritte Objects'; package: 'Gt4LlmCore' . This object is internally wrapped in a GtLMagritteBasedModel GtLWrappedModel << #GtLMagritteBasedModel slots: {}; package: 'Gt4LlmCore' . - The logic is implemented in GtLAbstractMagritteFunctionTool>>#performToolCall:inContext: performToolCall: aToolCall inContext: anExecutionContext | domainObject resultObject | aToolCall performedByTool: self. [ | arguments | (self jsonSchemaArgumentValidationErrorsFor: aToolCall) ifNotEmpty: [ :aCollectionOfErrors | resultObject := GtLMagritteBasedModel new name: 'Response'; model: aCollectionOfErrors first. aToolCall result: resultObject. ^ resultObject ]. arguments := self parametersDescription readGenericDomainObjectsFrom: aToolCall rawArgumentsJsonString. aToolCall arguments: arguments ] on: NeoJSONParseError do: [ :anException | aToolCall result: (GtLExceptionDomainObject freeze: anException ). ^ aToolCall result ]. domainObject := self privatePerformToolCall: aToolCall inContext: anExecutionContext. "Temporary check to enble the new LLM views if used" self class shouldEnableDataViews ifTrue: [ (domainObject llmDataViewsInContext: GtLDataViewContext new) ifNotEmpty: [ :llmViews | resultObject := GTLDataViewWrapperModel new targetObject: domainObject; targetDataView: llmViews. aToolCall result: resultObject. ^ resultObject ] ]. ((domainObject isKindOf: GtLDomainObject) not or: [ domainObject wasMovedToMagritteDescriptions ]) ifTrue: [ resultObject := GtLMagritteBasedModel new name: 'Response'; model: domainObject] ifFalse: [ resultObject := domainObject ]. aToolCall result: resultObject. ^ resultObject . Our sample tool implements the logic in GtLMagritteToolForMethodsSource>>#privatePerformToolCall:inContext: privatePerformToolCall: aToolCall inContext: anExecutionContext | arguments methodDescriptors methodObjects | arguments := aToolCall arguments ifNil: [ Dictionary new ]. methodDescriptors := arguments at: 'methodDescriptors'. methodObjects := methodDescriptors collect: [ :aMethodDescriptor | aMethodDescriptor methodObjectIfMissing: [ :anErrorResult | anErrorResult ] ]. ^ GtLMethodsWithSource new methods: methodObjects asArray .

Each tool has corresponding examples that check its logic in isolation. For example, GtLMagritteToolForMethodsSource GtLAbstractMagritteFunctionTool << #GtLMagritteToolForMethodsSource slots: {}; tag: 'Tools'; package: 'Gt4LlmCore' has examples in GtLMagritteToolForMethodsSourceExamples Object << #GtLMagritteToolForMethodsSourceExamples slots: {}; tag: 'Tools'; package: 'Gt4LlmCore-Examples' .