constlocationString=location?` at ${location}`:``;
console.log(`You have an event: ${nameString}${dateString}${locationString}`)
}
// function to be called on addresses
functionreportAddresses(address){
for(constfieldinaddress){
if(address[field]){
if(field==="city"){
constcity=address.city;
conststate=address.state?`, ${address.state}`:'';
constzip=address.zip?` ${address.zip}`:'';
console.log(`${city}${state}${zip}`);
break;
}else{
console.log(`${address[field]}`);
}
}
}
console.log(``);
}
asyncfunctionmain(){
constollama=newOllama();
constsystemprompt=`You will be given a text along with a prompt and a schema. You will have to extract the information requested in the prompt from the text and generate output in JSON observing the schema provided. If the schema shows a type of integer or number, you must only show a integer for that field. A string should always be a valid string. If a value is unknown, leave it empty. Output the JSON with extra spaces to ensure that it pretty prints.`
constschema={
"eventsQuantity":{
"type":"integer",
"description":"The number of events in the source text"
},
"addressesQuantity":{
"type":"integer",
"description":"The number of addresses in the source text"
},
"events":[{
name:{
"type":"string",
description:"Name of the event"
},
"date":{
"type":"string",
"description":"Date of the event"
},
"location":{
"type":"string",
"description":"Location of the event"
},
"extraInfo":{
"type":"string",
"description":"Any extra information that is provided about the event."
}
}],
"people":[{
"name":{
"type":"string",
"description":"Name of the person"
},
"company":{
"type":"string",
"description":"Name of the company where they work"
},
"street":{
"type":"string",
"description":"Street address of the person or company. This is only the street name and the numerical address. Do not include city, state, or zip of the address in this field."
},
"city":{
"type":"string",
"description":"City portion of the address of the person or company"
},
"state":{
"type":"string",
"description":"State portion of the address of the person or company"
},
"zip":{
"type":"string",
"description":"Zip code of the person or company"
},
"extraInfo":{
"type":"string",
"description":"Any extra information that is provided about the location."
constprompt=`The source text is a series of emails that have been put into a single file. They are separated by three dashes. Review the source text and determine the full address of the person sending each of the emails as well as any events that we need to track. If they provide a company address use that. If any extra info is provided, such as a description of the place, or a floor, add it to extraInfo. The first field in the address JSON is quantity of events and should be set to the number of events tracked and the second field should be set to the number of addresses tracked in the file. Don't stuff an event into the output that isn't an event. Only add data to the mostly appropriate field. Don't make up fields that aren't in the schema. If there isn't a value for a field, use null. Output should be in JSON.\n\nSchema: \n${JSON.stringify(schema,null,2)}\n\nSource Text:\n${textcontent}`
awaitollama.setModel("neural-chat");
ollama.setSystemPrompt(systemprompt);
ollama.setJSONFormat(true);
constdata=awaitollama.generate(prompt);
constoutput=JSON.parse(data.output);
constevents=output.events;
constaddresses=output.people;
console.log(`Here are your ${output.eventsQuantity} events:`);
// Set the system prompt to prepare the model to receive a prompt and a schema and set some rules for the output.
constsystemprompt=`You will be given a text along with a prompt and a schema. You will have to extract the information requested in the prompt from the text and generate output in JSON observing the schema provided. If the schema shows a type of integer or number, you must only show a integer for that field. A string should always be a valid string. If a value is unknown, leave it empty. Output the JSON with extra spaces to ensure that it pretty prints.`
constschema={
"people":[{
"name":{
"type":"string",
"description":"Name of the person"
},
"title":{
"type":"string",
"description":"Title of the person"
}
}],
}
// Depending on the model chosen, you may be limited by the size of the context window, so limit the context to 2000 words.
constprompt=`Review the source text and determine the 10 most important people to focus on. Then extract the name and title for those people. Output should be in JSON.\n\nSchema: \n${JSON.stringify(schema,null,2)}\n\nSource Text:\n${textcontent}`
awaitollama.setModel("neural-chat");
ollama.setSystemPrompt(systemprompt);
// setJSONFormat is the equivalent of setting 'format: json' in the API
thanks for letting me know that you are going to come today, November 16, for my tea party. My address is 123 Falk St on Bainbridge Island. I live in the house with the red door. I will be home all day so just come by whenever you want.
Fred
---
Great, send the check to our office at 1917 1st St, Seattle, WA 98101. I will let you know when we receive it.
Mark Richardson
Big Corp
---
We are looking forward to seeing you at our Local AI Meetup. It will be held on December 3. It will be at the offices of Enormous Co. Our address is 344 1st Ave, Seattle, WA 98101. We will be meeting in the conference room on the 3rd floor.
One of the features added to some models is 'function calling'. It's a bit of a confusing name. It's understandable if you think that means the model can call functions, but that's not what it means. Function calling simply means that the output of the model is formatted in JSON, using a preconfigured schema, and uses the expected types. Then your code can use the output of the model and call functions with it. Using the JSON format in Ollama, you can use any model for function calling.
The two examples provided can extract information out of the provided texts. The first example uses the first couple of chapters from War and Peace by Lev Nikolayevich Tolstoy, and extracts the names and titles of the characters introduced in the story. The second example uses a more complicated schema to pull out addresses and event information from a series of emails.
## Running the examples
1. Clone this repo and navigate to the `examples/typescript-functioncalling` directory.
2. Install the dependencies with `npm install`.
3. Review the `wp.txt` file.
4. Run `tsx extractwp.ts`.
5. Review the `info.txt` file.
6. Run `tsx extractemail.ts`.
## Review the Code
Both examples do roughly the same thing with different source material. They both use the same system prompt, which tells the model to expect some instructions and a schema. Then we inject the schema into the prompt and generate an answer.
The first example, `extractwp.ts`, outputs the resulting JSON to the console, listing the characters introduced at the start of War and Peace. The second example, `extractemail.ts`, is a bit more complicated, extracting two different types of information: addresses and events. It outputs the results to a JSON blob, then the addresses are handed off to one function called `reportAddresses` and the events are handed off to another function called `reportEvents`.
Notice that both examples are using the model from Intel called `neural-chat`. This is not a model tuned for function calling, yet it performs very well at this task.
## Next Steps
Try exporting some of your real emails to the input file and seeing how well the model does. Try pointing the first example at other books. You could even have it cycle through all the sections and maybe add up the number of times any character is seen throughout the book, determining the most important characters. You can also try out different models.