From f883e52ce7ce93d02b87577996e96f9c147ceb4d Mon Sep 17 00:00:00 2001 From: ParthSareen Date: Wed, 4 Dec 2024 16:03:08 -0800 Subject: [PATCH 1/6] Add examples --- .../structured_outputs/structured_outputs.ts | 50 ++++++++++ examples/tools/calculator.ts | 97 +++++++++++++++++++ .../tools/{tools.ts => flight-tracker.ts} | 6 +- 3 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 examples/structured_outputs/structured_outputs.ts create mode 100644 examples/tools/calculator.ts rename examples/tools/{tools.ts => flight-tracker.ts} (95%) diff --git a/examples/structured_outputs/structured_outputs.ts b/examples/structured_outputs/structured_outputs.ts new file mode 100644 index 0000000..24efa49 --- /dev/null +++ b/examples/structured_outputs/structured_outputs.ts @@ -0,0 +1,50 @@ +import { Ollama } from '../../src/index.js'; +import { z } from 'zod'; +import { zodToJsonSchema } from 'zod-to-json-schema'; + +const ollama = new Ollama(); + +// Define the schema for friend info +const FriendInfoSchema = z.object({ + name: z.string(), + age: z.number().int(), + is_available: z.boolean() +}); + +// Define the schema for friend list +const FriendListSchema = z.object({ + friends: z.array(FriendInfoSchema) +}); + +async function run() { + // Convert the Zod schema to JSON Schema format + const jsonSchema = zodToJsonSchema(FriendListSchema); + + // Can use manually defined schema directly + // const schema = { 'type': 'object', 'properties': { 'friends': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'name': { 'type': 'string' }, 'age': { 'type': 'integer' }, 'is_available': { 'type': 'boolean' } }, 'required': ['name', 'age', 'is_available'] } } }, 'required': ['friends'] } + + const messages = [{ + role: 'user', + content: 'I have two friends. The first is Ollama 22 years old busy saving the world, and the second is Alonso 23 years old and wants to hang out. Return a list of friends in JSON format' + }]; + + const response = await ollama.chat({ + model: 'llama3.1:8b', + messages: messages, + format: jsonSchema, // or format: schema + options: { + temperature: 0 // Make responses more deterministic + } + }); + + // Parse and validate the response + try { + console.log('\n', response.message.content, '\n'); + const friendsResponse = FriendListSchema.parse(JSON.parse(response.message.content)); + console.log('\n', friendsResponse, '\n'); + } catch (error) { + console.error("Generated invalid response:", error); + } +} + +run().catch(console.error); \ No newline at end of file diff --git a/examples/tools/calculator.ts b/examples/tools/calculator.ts new file mode 100644 index 0000000..01d40b4 --- /dev/null +++ b/examples/tools/calculator.ts @@ -0,0 +1,97 @@ +import { Ollama } from '../../src/index.js'; + +const ollama = new Ollama(); + +// Add two numbers function +function addTwoNumbers(args: { a: number, b: number }): number { + return args.a + args.b; +} + +// Subtract two numbers function +function subtractTwoNumbers(args: { a: number, b: number }): number { + return args.a - args.b; +} + +// Tool definition for add function +const addTwoNumbersTool = { + type: 'function', + function: { + name: 'addTwoNumbers', + description: 'Add two numbers together', + parameters: { + type: 'object', + required: ['a', 'b'], + properties: { + a: { type: 'number', description: 'The first number' }, + b: { type: 'number', description: 'The second number' } + } + } + } +}; + +// Tool definition for subtract function +const subtractTwoNumbersTool = { + type: 'function', + function: { + name: 'subtractTwoNumbers', + description: 'Subtract two numbers', + parameters: { + type: 'object', + required: ['a', 'b'], + properties: { + a: { type: 'number', description: 'The first number' }, + b: { type: 'number', description: 'The second number' } + } + } + } +}; + +async function run(model: string) { + const messages = [{ role: 'user', content: 'What is three minus one?' }]; + console.log('Prompt:', messages[0].content); + + const availableFunctions = { + addTwoNumbers: addTwoNumbers, + subtractTwoNumbers: subtractTwoNumbers + }; + + const response = await ollama.chat({ + model: model, + messages: messages, + tools: [addTwoNumbersTool, subtractTwoNumbersTool] + }); + + let output: number; + if (response.message.tool_calls) { + // Process tool calls from the response + for (const tool of response.message.tool_calls) { + const functionToCall = availableFunctions[tool.function.name]; + if (functionToCall) { + console.log('Calling function:', tool.function.name); + console.log('Arguments:', tool.function.arguments); + output = functionToCall(tool.function.arguments); + console.log('Function output:', output); + + // Add the function response to messages for the model to use + messages.push(response.message); + messages.push({ + role: 'tool', + content: output.toString(), + }); + } else { + console.log('Function', tool.function.name, 'not found'); + } + } + + // Get final response from model with function outputs + const finalResponse = await ollama.chat({ + model: model, + messages: messages + }); + console.log('Final response:', finalResponse.message.content); + } else { + console.log('No tool calls returned from model'); + } +} + +run('llama3.1:8b').catch(error => console.error("An error occurred:", error)); \ No newline at end of file diff --git a/examples/tools/tools.ts b/examples/tools/flight-tracker.ts similarity index 95% rename from examples/tools/tools.ts rename to examples/tools/flight-tracker.ts index 392d306..1e836cb 100644 --- a/examples/tools/tools.ts +++ b/examples/tools/flight-tracker.ts @@ -1,4 +1,7 @@ -import ollama from 'ollama'; +// import ollama from 'ollama'; +import { Ollama } from '../../src/index.js'; + +const ollama = new Ollama(); // Simulates an API call to get flight times // In a real application, this would fetch data from a live database or API @@ -70,6 +73,7 @@ async function run(model: string) { for (const tool of response.message.tool_calls) { const functionToCall = availableFunctions[tool.function.name]; const functionResponse = functionToCall(tool.function.arguments); + console.log('functionResponse', functionResponse) // Add function response to the conversation messages.push({ role: 'tool', From 592f1e4bf4b1954b54577c80d31a31fafd23ef54 Mon Sep 17 00:00:00 2001 From: ParthSareen Date: Thu, 5 Dec 2024 11:01:40 -0800 Subject: [PATCH 2/6] Update examples --- .../structured-outputs-image.ts | 78 +++++++++++++++++++ ...tured_outputs.ts => structured-outputs.ts} | 0 2 files changed, 78 insertions(+) create mode 100644 examples/structured_outputs/structured-outputs-image.ts rename examples/structured_outputs/{structured_outputs.ts => structured-outputs.ts} (100%) diff --git a/examples/structured_outputs/structured-outputs-image.ts b/examples/structured_outputs/structured-outputs-image.ts new file mode 100644 index 0000000..8067948 --- /dev/null +++ b/examples/structured_outputs/structured-outputs-image.ts @@ -0,0 +1,78 @@ +import { Ollama } from '../../src/index.js'; +import { z } from 'zod'; +import { zodToJsonSchema } from 'zod-to-json-schema'; +import { readFileSync } from 'fs'; +import { resolve } from 'path'; +import { createInterface } from 'readline'; + +const ollama = new Ollama(); + +// Define the schema for image objects +const ObjectSchema = z.object({ + name: z.string(), + confidence: z.number(), + attributes: z.record(z.any()).optional() +}); + +// Define the schema for image description +const ImageDescriptionSchema = z.object({ + summary: z.string(), + objects: z.array(ObjectSchema), + scene: z.string(), + colors: z.array(z.string()), + time_of_day: z.enum(['Morning', 'Afternoon', 'Evening', 'Night']), + setting: z.enum(['Indoor', 'Outdoor', 'Unknown']), + text_content: z.string().optional() +}); + +async function run() { + // Create readline interface for user input + const rl = createInterface({ + input: process.stdin, + output: process.stdout + }); + + // Get path from user input + const path = await new Promise(resolve => { + rl.question('Enter the path to your image: ', resolve); + }); + rl.close(); + + // Verify the file exists and read it + try { + const imagePath = resolve(path); + const imageBuffer = readFileSync(imagePath); + const base64Image = imageBuffer.toString('base64'); + + // Convert the Zod schema to JSON Schema format + const jsonSchema = zodToJsonSchema(ImageDescriptionSchema); + + const messages = [{ + role: 'user', + content: 'Analyze this image and return a detailed JSON description including objects, scene, colors and any text detected. If you cannot determine certain details, leave those fields empty.', + images: [base64Image] + }]; + + const response = await ollama.chat({ + model: 'llama3.2-vision', + messages: messages, + format: jsonSchema, + options: { + temperature: 0 // Make responses more deterministic + } + }); + + // Parse and validate the response + try { + const imageAnalysis = ImageDescriptionSchema.parse(JSON.parse(response.message.content)); + console.log('\nImage Analysis:', imageAnalysis, '\n'); + } catch (error) { + console.error("Generated invalid response:", error); + } + + } catch (error) { + console.error("Error reading image file:", error); + } +} + +run().catch(console.error); diff --git a/examples/structured_outputs/structured_outputs.ts b/examples/structured_outputs/structured-outputs.ts similarity index 100% rename from examples/structured_outputs/structured_outputs.ts rename to examples/structured_outputs/structured-outputs.ts From 02ecd355b6899b4f378fdc128622f59de1d75a9f Mon Sep 17 00:00:00 2001 From: ParthSareen Date: Thu, 5 Dec 2024 12:00:04 -0800 Subject: [PATCH 3/6] Update examples --- .../structured-outputs-image.ts | 37 +++++++++------- .../structured_outputs/structured-outputs.ts | 43 ++++++++++++++----- examples/tools/flight-tracker.ts | 4 +- 3 files changed, 54 insertions(+), 30 deletions(-) diff --git a/examples/structured_outputs/structured-outputs-image.ts b/examples/structured_outputs/structured-outputs-image.ts index 8067948..e6fcc6f 100644 --- a/examples/structured_outputs/structured-outputs-image.ts +++ b/examples/structured_outputs/structured-outputs-image.ts @@ -1,31 +1,36 @@ -import { Ollama } from '../../src/index.js'; +import ollama from 'ollama'; + import { z } from 'zod'; import { zodToJsonSchema } from 'zod-to-json-schema'; import { readFileSync } from 'fs'; import { resolve } from 'path'; import { createInterface } from 'readline'; -const ollama = new Ollama(); +/* + Ollama vision capabilities with structured outputs + It takes an image file as input and returns a structured JSON description of the image contents + including detected objects, scene analysis, colors, and any text found in the image +*/ // Define the schema for image objects const ObjectSchema = z.object({ - name: z.string(), - confidence: z.number(), - attributes: z.record(z.any()).optional() + name: z.string().describe('The name of the object'), + confidence: z.number().min(0).max(1).describe('The confidence score of the object detection'), + attributes: z.record(z.any()).optional().describe('Additional attributes of the object') }); -// Define the schema for image description +// Schema for individual objects detected in the image const ImageDescriptionSchema = z.object({ - summary: z.string(), - objects: z.array(ObjectSchema), - scene: z.string(), - colors: z.array(z.string()), - time_of_day: z.enum(['Morning', 'Afternoon', 'Evening', 'Night']), - setting: z.enum(['Indoor', 'Outdoor', 'Unknown']), - text_content: z.string().optional() + summary: z.string().describe('A concise summary of the image'), + objects: z.array(ObjectSchema).describe('An array of objects detected in the image'), + scene: z.string().describe('The scene of the image'), + colors: z.array(z.string()).describe('An array of colors detected in the image'), + time_of_day: z.enum(['Morning', 'Afternoon', 'Evening', 'Night']).describe('The time of day the image was taken'), + setting: z.enum(['Indoor', 'Outdoor', 'Unknown']).describe('The setting of the image'), + text_content: z.string().describe('Any text detected in the image') }); -async function run() { +async function run(model: string) { // Create readline interface for user input const rl = createInterface({ input: process.stdin, @@ -54,7 +59,7 @@ async function run() { }]; const response = await ollama.chat({ - model: 'llama3.2-vision', + model: model, messages: messages, format: jsonSchema, options: { @@ -75,4 +80,4 @@ async function run() { } } -run().catch(console.error); +run('llama3.2-vision').catch(console.error); \ No newline at end of file diff --git a/examples/structured_outputs/structured-outputs.ts b/examples/structured_outputs/structured-outputs.ts index 24efa49..1a7554d 100644 --- a/examples/structured_outputs/structured-outputs.ts +++ b/examples/structured_outputs/structured-outputs.ts @@ -1,27 +1,48 @@ -import { Ollama } from '../../src/index.js'; +import ollama from 'ollama'; import { z } from 'zod'; import { zodToJsonSchema } from 'zod-to-json-schema'; -const ollama = new Ollama(); +/* + Ollama structured outputs capabilities + It parses the response from the model into a structured JSON object using Zod +*/ // Define the schema for friend info const FriendInfoSchema = z.object({ - name: z.string(), - age: z.number().int(), - is_available: z.boolean() + name: z.string().describe('The name of the friend'), + age: z.number().int().describe('The age of the friend'), + is_available: z.boolean().describe('Whether the friend is available') }); // Define the schema for friend list const FriendListSchema = z.object({ - friends: z.array(FriendInfoSchema) + friends: z.array(FriendInfoSchema).describe('An array of friends') }); -async function run() { +async function run(model: string) { // Convert the Zod schema to JSON Schema format const jsonSchema = zodToJsonSchema(FriendListSchema); - // Can use manually defined schema directly - // const schema = { 'type': 'object', 'properties': { 'friends': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'name': { 'type': 'string' }, 'age': { 'type': 'integer' }, 'is_available': { 'type': 'boolean' } }, 'required': ['name', 'age', 'is_available'] } } }, 'required': ['friends'] } + /* Can use manually defined schema directly + const schema = { + 'type': 'object', + 'properties': { + 'friends': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'name': { 'type': 'string' }, + 'age': { 'type': 'integer' }, + 'is_available': { 'type': 'boolean' } + }, + 'required': ['name', 'age', 'is_available'] + } + } + }, + 'required': ['friends'] + } + */ const messages = [{ role: 'user', @@ -29,7 +50,7 @@ async function run() { }]; const response = await ollama.chat({ - model: 'llama3.1:8b', + model: model, messages: messages, format: jsonSchema, // or format: schema options: { @@ -47,4 +68,4 @@ async function run() { } } -run().catch(console.error); \ No newline at end of file +run('llama3.1:8b').catch(console.error); \ No newline at end of file diff --git a/examples/tools/flight-tracker.ts b/examples/tools/flight-tracker.ts index 1e836cb..63ed61f 100644 --- a/examples/tools/flight-tracker.ts +++ b/examples/tools/flight-tracker.ts @@ -1,7 +1,5 @@ -// import ollama from 'ollama'; -import { Ollama } from '../../src/index.js'; +import ollama from 'ollama'; -const ollama = new Ollama(); // Simulates an API call to get flight times // In a real application, this would fetch data from a live database or API From 45e336502e0deb17ea9823e9d230f71b33a856d8 Mon Sep 17 00:00:00 2001 From: ParthSareen Date: Thu, 5 Dec 2024 12:10:40 -0800 Subject: [PATCH 4/6] Small fixes --- examples/structured_outputs/structured-outputs-image.ts | 2 +- examples/structured_outputs/structured-outputs.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/structured_outputs/structured-outputs-image.ts b/examples/structured_outputs/structured-outputs-image.ts index e6fcc6f..467de2e 100644 --- a/examples/structured_outputs/structured-outputs-image.ts +++ b/examples/structured_outputs/structured-outputs-image.ts @@ -70,7 +70,7 @@ async function run(model: string) { // Parse and validate the response try { const imageAnalysis = ImageDescriptionSchema.parse(JSON.parse(response.message.content)); - console.log('\nImage Analysis:', imageAnalysis, '\n'); + console.log('Image Analysis:', imageAnalysis); } catch (error) { console.error("Generated invalid response:", error); } diff --git a/examples/structured_outputs/structured-outputs.ts b/examples/structured_outputs/structured-outputs.ts index 1a7554d..b55dbbe 100644 --- a/examples/structured_outputs/structured-outputs.ts +++ b/examples/structured_outputs/structured-outputs.ts @@ -60,9 +60,8 @@ async function run(model: string) { // Parse and validate the response try { - console.log('\n', response.message.content, '\n'); const friendsResponse = FriendListSchema.parse(JSON.parse(response.message.content)); - console.log('\n', friendsResponse, '\n'); + console.log(friendsResponse); } catch (error) { console.error("Generated invalid response:", error); } From 0bb270d76660b8c81d284d3b8e3acf1a0046af60 Mon Sep 17 00:00:00 2001 From: Parth Sareen Date: Thu, 5 Dec 2024 12:54:09 -0800 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Bruce MacDonald --- examples/tools/calculator.ts | 4 +--- examples/tools/flight-tracker.ts | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/tools/calculator.ts b/examples/tools/calculator.ts index 01d40b4..5160256 100644 --- a/examples/tools/calculator.ts +++ b/examples/tools/calculator.ts @@ -1,6 +1,4 @@ -import { Ollama } from '../../src/index.js'; - -const ollama = new Ollama(); +import ollama from 'ollama'; // Add two numbers function function addTwoNumbers(args: { a: number, b: number }): number { diff --git a/examples/tools/flight-tracker.ts b/examples/tools/flight-tracker.ts index 63ed61f..7b7bc43 100644 --- a/examples/tools/flight-tracker.ts +++ b/examples/tools/flight-tracker.ts @@ -1,6 +1,5 @@ import ollama from 'ollama'; - // Simulates an API call to get flight times // In a real application, this would fetch data from a live database or API function getFlightTimes(args: { [key: string]: any }) { From 60c3d38bdb59fc85636bf7a7c1e6fba852eec4c3 Mon Sep 17 00:00:00 2001 From: ParthSareen Date: Thu, 5 Dec 2024 12:54:53 -0800 Subject: [PATCH 6/6] Address comments --- examples/structured_outputs/structured-outputs-image.ts | 2 +- examples/structured_outputs/structured-outputs.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/structured_outputs/structured-outputs-image.ts b/examples/structured_outputs/structured-outputs-image.ts index 467de2e..89fea3f 100644 --- a/examples/structured_outputs/structured-outputs-image.ts +++ b/examples/structured_outputs/structured-outputs-image.ts @@ -12,7 +12,7 @@ import { createInterface } from 'readline'; including detected objects, scene analysis, colors, and any text found in the image */ -// Define the schema for image objects +// Schema for individual objects detected in the image const ObjectSchema = z.object({ name: z.string().describe('The name of the object'), confidence: z.number().min(0).max(1).describe('The confidence score of the object detection'), diff --git a/examples/structured_outputs/structured-outputs.ts b/examples/structured_outputs/structured-outputs.ts index b55dbbe..855806e 100644 --- a/examples/structured_outputs/structured-outputs.ts +++ b/examples/structured_outputs/structured-outputs.ts @@ -1,4 +1,5 @@ import ollama from 'ollama'; + import { z } from 'zod'; import { zodToJsonSchema } from 'zod-to-json-schema';