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>
	| call targetObject |
	call := self
		executeTool: GtLMagritteToolForMethodsSource new
		withArguments: {
			'methodDescriptors' -> {
				{'methodClassName' -> 'Collection'.
					'methodName' -> 'withAll:'.
					'classSide' -> true} asDictionary.
				{'methodClassName' -> #GtLMagritteToolForMethodsSourceExamples.
					'methodName' -> 'methodSourcesForExistingMethods'.
					'classSide' -> false} asDictionary
			} asArray
		} asDictionary.
	targetObject := call result targetObject.
	self assert: (targetObject isKindOf: GtLMethodsWithSource).
	self assert: targetObject methods size equals: 2.
	self
		assert: targetObject methods first sourceCode
		equals: (Collection class>>#withAll:) sourceCode.
	self
		assert: targetObject 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: {}; tag: 'Core'; 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 := self resultObjectFromDomainObject: aCollectionOfErrors first. aToolCall result: resultObject. ^ resultObject ]. arguments := self parametersDescription readGenericDomainObjectsFrom: aToolCall rawArgumentsJsonString. aToolCall arguments: arguments ] on: NeoJSONParseError do: [ :anException | aToolCall result: (self resultObjectFromDomainObject:(GtLExceptionDomainObject freeze: anException )). ^ aToolCall result ]. domainObject := self privatePerformToolCall: aToolCall inContext: anExecutionContext. resultObject := self resultObjectFromDomainObject: 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 GtLAbstractMagritteFunctionToolExamples << #GtLMagritteToolForMethodsSourceExamples slots: {}; tag: 'Tools'; package: 'Gt4LlmCore-Examples' .