Skip to content

Commit

Permalink
support multi-step tool executions
Browse files Browse the repository at this point in the history
  • Loading branch information
EvanBoyle committed Feb 1, 2025
1 parent 4d40d5a commit ea81d56
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 28 deletions.
80 changes: 80 additions & 0 deletions examples/openai-vNext/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,82 @@ async function structuredOutput() {
return results;
}

async function multiStepTools() {
// Weather tool (reusing existing schema)
const weatherSchema = z.object({
location: z.string(),
});

const weatherTool = new GSXTool<typeof weatherSchema>(
"get_weather",
"Get the current weather for a location",
weatherSchema,
async ({ location }) => {
console.log("Getting weather for", location);
// Simulate API delay
const weather = ["sunny", "cloudy", "rainy", "snowy"];
return Promise.resolve({
weather: weather[Math.floor(Math.random() * weather.length)],
});
},
);

// Local services tool
const servicesSchema = z.object({
service: z.enum(["restaurants", "parks", "cafes"]),
location: z.string(),
});

const servicesTool = new GSXTool<typeof servicesSchema>(
"find_local_services",
"Find local services (restaurants, parks, or cafes) in a given location",
servicesSchema,
async ({ service, location }) => {
console.log(`Finding ${service} near ${location}`);
// Simulate API delay
const places = {
restaurants: ["Tasty Bites", "Gourmet Corner", "Local Flavor"],
parks: ["Central Park", "Riverside Walk", "Community Garden"],
cafes: ["Coffee Haven", "Bean Scene", "Morning Brew"],
};
return Promise.resolve({
places: places[service].map((name) => ({
name,
rating: Math.floor(Math.random() * 2) + 4, // 4-5 star rating
})),
});
},
);

const results = await gsx.workflow<ChatCompletionOutput>(
<OpenAIProvider apiKey={process.env.OPENAI_API_KEY}>
<GSXCompletion
messages={[
{
role: "system",
content:
"You are a helpful local guide who provides detailed neighborhood analysis.",
},
{
role: "user",
content: `I'm thinking of spending a day in downtown Seattle. Can you:
1. Check the weather first
2. Based on the weather, suggest some activities (use the local services tool)
3. If it's good weather, look for parks and outdoor dining
4. If it's bad weather, focus on indoor places like cafes and restaurants
Please explain your thinking as you go through this analysis.`,
},
]}
model="gpt-4"
temperature={0.7}
tools={[weatherTool, servicesTool]}
/>
</OpenAIProvider>,
);

return results;
}

async function main() {
console.log("basic completion 🔥");
const r = await basicCompletion();
Expand Down Expand Up @@ -247,6 +323,10 @@ async function main() {
const structured = await structuredOutput();
console.log(structured.overallVerdict);
console.log(structured);

console.log("\nmulti-step tools completion 🔥");
const multiStepResults = await multiStepTools();
console.log(multiStepResults.choices[0].message.content);
}

main().catch(console.error);
59 changes: 31 additions & 28 deletions packages/gensx-openai/src/gsx-completion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -236,39 +236,42 @@ export const ToolsCompletion = gsx.Component<
ToolsCompletionOutput
>("ToolsCompletion", async (props) => {
const { tools, ...rest } = props;
const currentMessages = [...rest.messages];

// Make initial completion to get tool calls
const completion = await gsx.execute<ChatCompletionOutput>(
<OpenAIChatCompletion {...rest} tools={tools} />,
// Make initial completion
let completion = await gsx.execute<ChatCompletionOutput>(
<OpenAIChatCompletion {...rest} messages={currentMessages} tools={tools} />,
);

const toolCalls = completion.choices[0].message.tool_calls;
// If no tool calls, return the completion
if (!toolCalls?.length) {
return completion;
}
// Keep processing tool calls until none are left
while (completion.choices[0].message.tool_calls?.length) {
// Add assistant's message to the conversation
currentMessages.push(completion.choices[0].message);

// Execute tools
const toolResponses = await gsx.execute<ChatCompletionMessageParam[]>(
<ToolExecutor
tools={tools}
toolCalls={toolCalls}
messages={[...rest.messages, completion.choices[0].message]}
model={rest.model}
/>,
);
// Execute tools
const toolResponses = await gsx.execute<ChatCompletionMessageParam[]>(
<ToolExecutor
tools={tools}
toolCalls={completion.choices[0].message.tool_calls}
messages={currentMessages}
model={rest.model}
/>,
);

// Make final completion with tool results
return gsx.execute<ChatCompletionOutput>(
<OpenAIChatCompletion
{...rest}
messages={[
...rest.messages,
completion.choices[0].message,
...toolResponses,
]}
/>,
);
// Add tool responses to the conversation
currentMessages.push(...toolResponses);

// Make next completion
completion = await gsx.execute<ChatCompletionOutput>(
<OpenAIChatCompletion
{...rest}
messages={currentMessages}
tools={tools}
/>,
);
}

return completion;
});

// Combined structured output component
Expand Down

0 comments on commit ea81d56

Please sign in to comment.