Skip to content

Commit d1fb561

Browse files
committed
add more examples
1 parent d224d00 commit d1fb561

File tree

10 files changed

+728
-621
lines changed

10 files changed

+728
-621
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/usr/bin/env -S npm run tsn -T
2+
3+
import OpenAI from 'openai';
4+
import { betaZodTool } from 'openai/helpers/beta/zod';
5+
import { z } from 'zod';
6+
7+
const client = new OpenAI();
8+
9+
async function main() {
10+
const runner = client.chat.completions.toolRunner({
11+
messages: [
12+
{
13+
role: 'user',
14+
content: `I'm planning a trip to San Francisco and I need some information. Can you help me with the weather, current time, and currency exchange rates (from EUR)? Please use parallel tool use`,
15+
},
16+
],
17+
tools: [
18+
betaZodTool({
19+
name: 'getWeather',
20+
description: 'Get the weather at a specific location',
21+
inputSchema: z.object({
22+
location: z.string().describe('The city and state, e.g. San Francisco, CA'),
23+
}),
24+
run: ({ location }) => {
25+
return `The weather is sunny with a temperature of 20°C in ${location}.`;
26+
},
27+
}),
28+
betaZodTool({
29+
name: 'getTime',
30+
description: 'Get the current time in a specific timezone',
31+
inputSchema: z.object({
32+
timezone: z.string().describe('The timezone, e.g. America/Los_Angeles'),
33+
}),
34+
run: ({ timezone }) => {
35+
return `The current time in ${timezone} is 3:00 PM.`;
36+
},
37+
}),
38+
betaZodTool({
39+
name: 'getCurrencyExchangeRate',
40+
description: 'Get the exchange rate between two currencies',
41+
inputSchema: z.object({
42+
from_currency: z.string().describe('The currency to convert from, e.g. USD'),
43+
to_currency: z.string().describe('The currency to convert to, e.g. EUR'),
44+
}),
45+
run: ({ from_currency, to_currency }) => {
46+
return `The exchange rate from ${from_currency} to ${to_currency} is 0.85.`;
47+
},
48+
}),
49+
],
50+
model: 'gpt-4o',
51+
max_tokens: 1024,
52+
// This limits the conversation to at most 10 back and forth between the API.
53+
max_iterations: 10,
54+
stream: true,
55+
});
56+
57+
console.log(`\n🚀 Running tools...\n`);
58+
59+
let prevMessageStarted = '';
60+
let prevToolStarted = '';
61+
let prevWasToolCall = false;
62+
63+
for await (const messageStream of runner) {
64+
for await (const event of messageStream) {
65+
const hadToolCalls = !!event.choices?.[0]?.delta?.tool_calls;
66+
67+
if (hadToolCalls) {
68+
if (!prevMessageStarted) {
69+
console.log(`┌─ Message ${event.id} `.padEnd(process.stdout.columns, '─'));
70+
prevMessageStarted = event.id;
71+
}
72+
73+
prevWasToolCall = true;
74+
const toolCalls = event.choices[0]!.delta.tool_calls!;
75+
76+
for (const toolCall of toolCalls) {
77+
if (toolCall.function?.name && prevToolStarted !== toolCall.function.name) {
78+
process.stdout.write(`\n${toolCall.function.name}: `);
79+
prevToolStarted = toolCall.function.name;
80+
} else if (toolCall.function?.arguments) {
81+
process.stdout.write(toolCall.function.arguments);
82+
}
83+
}
84+
} else if (event.choices?.[0]?.delta?.content) {
85+
if (prevWasToolCall) {
86+
console.log();
87+
console.log();
88+
console.log(`└─`.padEnd(process.stdout.columns, '─'));
89+
console.log();
90+
prevWasToolCall = false;
91+
}
92+
93+
if (prevMessageStarted !== event.id) {
94+
console.log(`┌─ Message ${event.id} `.padEnd(process.stdout.columns, '─'));
95+
console.log();
96+
prevMessageStarted = event.id;
97+
}
98+
99+
process.stdout.write(event.choices[0].delta.content);
100+
}
101+
}
102+
}
103+
104+
console.log();
105+
console.log();
106+
console.log(`└─`.padEnd(process.stdout.columns, '─'));
107+
}
108+
109+
main();

examples/tool-helpers-advanced.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/usr/bin/env -S npm run tsn -T
2+
3+
import OpenAI from 'openai';
4+
import { betaZodTool } from 'openai/helpers/beta/zod';
5+
import { z } from 'zod';
6+
7+
const client = new OpenAI();
8+
9+
async function main() {
10+
const message = await client.chat.completions.toolRunner({
11+
messages: [
12+
{
13+
role: 'user',
14+
content: `What is the weather in SF?`,
15+
},
16+
],
17+
tools: [
18+
betaZodTool({
19+
name: 'getWeather',
20+
description: 'Get the weather at a specific location',
21+
inputSchema: z.object({
22+
location: z.string().describe('The city and state, e.g. San Francisco, CA'),
23+
}),
24+
run: ({ location }) => {
25+
return `The weather is foggy with a temperature of 20°C in ${location}.`;
26+
},
27+
}),
28+
],
29+
model: 'gpt-4o',
30+
max_tokens: 1024,
31+
// the maximum number of iterations to run the tool
32+
max_iterations: 10,
33+
});
34+
35+
console.log('Final response:', message.content);
36+
}
37+
38+
main();
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env -S npm run tsn -T
2+
3+
import OpenAI from 'openai';
4+
import { betaTool } from 'openai/helpers/beta/json-schema';
5+
6+
const client = new OpenAI();
7+
8+
async function main() {
9+
const message = await client.chat.completions.toolRunner({
10+
messages: [
11+
{
12+
role: 'user',
13+
content: `What is the weather in SF?`,
14+
},
15+
],
16+
tools: [
17+
betaTool({
18+
name: 'getWeather',
19+
description: 'Get the weather at a specific location',
20+
inputSchema: {
21+
type: 'object',
22+
properties: {
23+
location: {
24+
type: 'string',
25+
description: 'The city and state, e.g. San Francisco, CA',
26+
},
27+
},
28+
required: ['location'],
29+
},
30+
run: ({ location }) => {
31+
return `The weather is foggy with a temperature of 20°C in ${location}.`;
32+
},
33+
}),
34+
],
35+
model: 'gpt-4o',
36+
max_tokens: 1024,
37+
// the maximum number of iterations to run the tool
38+
max_iterations: 10,
39+
});
40+
41+
console.log('Final response:', message.content);
42+
}
43+
44+
main();

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"fix": "./scripts/format"
2828
},
2929
"dependencies": {
30+
"json-schema-to-ts": "3.1.1",
3031
"nock": "^14.0.10"
3132
},
3233
"devDependencies": {

src/helpers/beta/betaZodTool.ts

Whitespace-only changes.

src/helpers/beta/json-schema.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import type { FromSchema, JSONSchema } from 'json-schema-to-ts';
2+
import type { BetaRunnableTool, Promisable } from '../../lib/beta/BetaRunnableTool';
3+
import type { FunctionTool } from '../../resources/beta';
4+
import { OpenAIError } from '../../error';
5+
6+
type NoInfer<T> = T extends infer R ? R : never;
7+
8+
/**
9+
* Creates a Tool with a provided JSON schema that can be passed
10+
* to the `.toolRunner()` method. The schema is used to automatically validate
11+
* the input arguments for the tool.
12+
*/
13+
export function betaTool<const Schema extends Exclude<JSONSchema, boolean> & { type: 'object' }>(options: {
14+
name: string;
15+
inputSchema: Schema;
16+
description: string;
17+
run: (args: NoInfer<FromSchema<Schema>>) => Promisable<string | Array<FunctionTool>>;
18+
}): BetaRunnableTool<NoInfer<FromSchema<Schema>>> {
19+
if (options.inputSchema.type !== 'object') {
20+
throw new OpenAIError(
21+
`JSON schema for tool "${options.name}" must be an object, but got ${options.inputSchema.type}`,
22+
);
23+
}
24+
25+
return {
26+
type: 'function',
27+
function: {
28+
name: options.name,
29+
parameters: options.inputSchema,
30+
description: options.description,
31+
},
32+
run: options.run,
33+
parse: (content: unknown) => content as FromSchema<Schema>,
34+
} as any; // For some reason this causes infinite inference so we cast to any to not crash lsp
35+
}

src/lib/beta-parser.ts

Lines changed: 0 additions & 105 deletions
This file was deleted.

src/lib/beta/BetaToolRunner.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import type {
1212
ChatCompletionToolMessageParam,
1313
} from '../../resources/chat/completions';
1414
import type { BetaRunnableTool } from './BetaRunnableTool';
15-
// BetaMessage, BetaMessageParam, BetaToolUnion, MessageCreateParams
16-
// import { BetaMessageStream } from '../BetaMessageStream';
1715

1816
/**
1917
* Just Promise.withResolvers(), which is not available in all environments.

tests/lib/tools/ToolRunner.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ function betaMessageToStreamEvents(message: ChatCompletion): ChatCompletionChunk
120120
const messageContent = message.choices[0]!.message;
121121

122122
// Check if it's a text message
123-
if (messageContent.content && typeof messageContent.content === 'string') {
123+
if (messageContent.content) {
124124
// Initial chunk with role only (no content in first chunk for text messages)
125125
events.push({
126126
choices: message.choices.map(

0 commit comments

Comments
 (0)