-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathToolsLearn.js
More file actions
147 lines (121 loc) · 4.19 KB
/
Copy pathToolsLearn.js
File metadata and controls
147 lines (121 loc) · 4.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import 'dotenv/config';
import * as z from 'zod';
import { tool } from '@langchain/core/tools';
import { ChatOpenRouter } from '@langchain/openrouter';
import { HumanMessage, SystemMessage, ToolMessage } from '@langchain/core/messages';
const getCoordinates = tool(
async ({ location }) => {
const url =
`https://geocoding-api.open-meteo.com/v1/search` +
`?name=${encodeURIComponent(location)}` +
`&count=1&language=en&format=json`;
const res = await fetch(url);
if (!res.ok) throw new Error(`Geocoding failed: ${res.status}`);
const data = await res.json();
const place = data.results?.[0];
if (!place) {
return `No coordinates found for "${location}".`;
}
return JSON.stringify({
name: place.name,
country: place.country,
latitude: place.latitude,
longitude: place.longitude,
});
},
{
name: 'get_coordinates',
description: 'Convert a location name into latitude and longitude.',
schema: z.object({
location: z.string().describe('The name of a place'),
}),
}
);
const getWeather = tool(
async ({ latitude, longitude }) => {
const url =
`https://api.open-meteo.com/v1/forecast` +
`?latitude=${latitude}&longitude=${longitude}` +
`¤t=temperature_2m,wind_speed_10m,precipitation,rain,showers,snowfall` +
`&daily=temperature_2m_max,temperature_2m_min,precipitation_probability_max` +
`&temperature_unit=fahrenheit&wind_speed_unit=mph&forecast_days=1`;
const res = await fetch(url);
if (!res.ok) throw new Error(`Weather request failed: ${res.status}`);
const data = await res.json();
return JSON.stringify({
current: data.current,
today: {
maxTempF: data.daily.temperature_2m_max[0],
minTempF: data.daily.temperature_2m_min[0],
precipitationChance: data.daily.precipitation_probability_max[0],
},
});
},
{
name: 'get_weather',
description: 'Get current weather and today’s forecast for coordinates.',
schema: z.object({
latitude: z.number().describe('Latitude'),
longitude: z.number().describe('Longitude'),
}),
}
);
const tools = [getCoordinates, getWeather];
const toolsByName = {
get_coordinates: getCoordinates,
get_weather: getWeather,
};
const model = new ChatOpenRouter({
model: 'cohere/north-mini-code:free',
apiKey: process.env.OPENROUTER_API_KEY,
});
const modelWithTools = model.bindTools(tools);
const messages = [
new SystemMessage(`
You are a hiking expert.
When the user asks what to wear for a hike use the get_coordinates tool to convert the location into coordinates.
Then ese get_weather tool with those coordinates. Recommend clothing based on temperature, wind, precipitation, and hiking comfort. Note any additional things you determinte would be important to the user and summarize your answer in roughly 5 sentances maximum.
`),
new HumanMessage('What should I wear on a hike in Santa Cruz today?'),
];
let response = await modelWithTools.invoke(messages);
messages.push(response);
while (response.tool_calls?.length) {
for (const toolCall of response.tool_calls) {
const selectedTool = toolsByName[toolCall.name];
if (!selectedTool) {
throw new Error(`Unknown tool requested: ${toolCall.name}`);
}
const result = await selectedTool.invoke(toolCall.args);
messages.push(
new ToolMessage({
content: String(result),
tool_call_id: toolCall.id,
name: toolCall.name,
})
);
}
response = await modelWithTools.invoke(messages);
messages.push(response);
}
// Stream the final response after all tools have been executed.
const stream = await modelWithTools.streamEvents(messages, {
version: "v2",
});
process.stdout.write("\nFinal answer:\n\n");
for await (const event of stream) {
if (event.event === "on_chat_model_stream") {
const chunk = event.data?.chunk?.content;
if (typeof chunk === "string") {
process.stdout.write(chunk);
} else if (Array.isArray(chunk)) {
// Some models stream arrays of content blocks
for (const block of chunk) {
if (block.type === "text") {
process.stdout.write(block.text);
}
}
}
}
}
process.stdout.write("\n");