Public
Edited
Sep 28, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
getWebLLMCompletionStream(webllmParams.systemPrompt, webllmParams.prompt, {
temperature: webllmParams.temperature,
model: webllmParams.model
})
Insert cell
Insert cell
webllm = import("https://esm.run/@mlc-ai/web-llm@0.2.71")
Insert cell
availableModels = webllm.prebuiltAppConfig.model_list.map((m) => m.model_id)
Insert cell
engine = {
const setLabel = (id, text) => {
const label = document.getElementById(id);
if (label == null) {
throw Error("Cannot find label " + id);
}
label.innerText = text;
};

const initProgressCallback = (report) => {
setLabel("model-load", report.text);
};

return webllm.CreateMLCEngine(selectedModel, {
initProgressCallback: initProgressCallback
});
}
Insert cell
async function getWebLLMCompletionStream(
systemPrompt,
prompt,
{ temperature = 0.0 } = {}
) {
const outputElement = document.getElementById("webllm-output");
outputElement.textContent = "";

if (prompt.length == 0) {
return;
}

let messages = [];
if (systemPrompt) {
messages.push({ role: "system", content: systemPrompt });
}
messages.push({ role: "user", content: prompt });

const stream = await engine.chat.completions.create({
messages: messages,
temperature: temperature,
stream: true
});

for await (const part of stream) {
const text = part.choices[0]?.delta?.content || "";
outputElement.textContent += text;
}
console.log(outputElement.textContent);

const functionCall = extractJsonFromFunctionTags(outputElement.textContent);
console.log(functionCall);

try {
if (!functionCall || !functionCall.name || !functionCall.parameters) {
return;
} else {
const toolResponse = await callAPI(
functionCall.name,
functionCall.parameters
);
console.log(toolResponse);

messages.push({ role: "assistant", content: outputElement.textContent });
messages.push({
role: "user",
content: `Your function call was successful. Please read the output below and use this context to respond to my original query in natural language.

Output from function call \`${functionCall.name}(${
functionCall.parameters
})\`: ${JSON.stringify(toolResponse)}.`
});

const stream2 = await engine.chat.completions.create({
messages: messages,
temperature: temperature,
stream: true
});

outputElement.textContent += "\n";
for await (const part of stream2) {
const text = part.choices[0]?.delta?.content || "";
outputElement.textContent += text;
}
}
} catch (error) {
console.error(`Error during the following function call: ${functionCall}`);
}
}
Insert cell
function extractJsonFromFunctionTags(textContent) {
const regex = /<function>(.*?)<\/function>/s;
const match = regex.exec(textContent);

if (match) {
const jsonContent = match[1];

try {
return JSON.parse(jsonContent);
} catch (error) {
return null;
}
} else {
return null;
}
}
Insert cell
async function callAPI(apiName, params) {
switch (apiName) {
case "getWeeklyWeatherUSA":
try {
const city = params.city;
const state = params.state;
return await getWeeklyWeatherUSA(city, state);
} catch (error) {
console.error("Error parsing parameters or fetching weather:", error);
return Promise.reject("Error processing getWeeklyWeatherUSA request.");
}

default:
return Promise.resolve("No functions found by that name");
}
}
Insert cell
async function getWeeklyWeatherUSA(city, state) {
// Geocoding using OpenStreetMap Nominatim API
const geoUrl = `https://nominatim.openstreetmap.org/search?city=${city}&state=${state}&country=US&format=json&limit=1`;

try {
const geoResponse = await fetch(geoUrl, {
method: "GET"
});

const geoData = await geoResponse.json();

if (!geoData.length) {
throw new Error("Unable to geocode the given city and state.");
}

const { lat, lon } = geoData[0];

// NWS Weather Forecast Office (WFO) grid forecast endpoint from latitude and longitude
const gridForecastEndpointUrl = `https://api.weather.gov/points/${lat},${lon}`;
const gridForecastEndpointResponse = await fetch(gridForecastEndpointUrl);
const gridForecastEndpointData = await gridForecastEndpointResponse.json();

// Weather data using the grid forecast endpoint
const weatherUrl = gridForecastEndpointData.properties.forecast;
const weatherResponse = await fetch(weatherUrl);
const weatherData = (await weatherResponse.json()).properties.periods;

// Return filtered object
const filterKeys = (obj, keysToKeep) => {
return Object.keys(obj).reduce((accumulator, key) => {
if (keysToKeep.includes(key)) {
accumulator[key] = obj[key];
}
return accumulator;
}, {});
};

const dataToKeep = [
"name",
"temperature",
"temperatureUnit",
"probabilityOfPrecipitation",
"windSpeed",
"windDirection",
"shortForecast",
"detailedForecast"
];

const filteredWeatherData = weatherData.map((obj) =>
filterKeys(obj, dataToKeep)
);

return filteredWeatherData;
} catch (error) {
console.error("Error fetching data:", error);
return null;
}
}
Insert cell
tools = [weatherApiSpec]
Insert cell
Insert cell
systemPromptForToolUse = `You are a helpful assistant that can call relevant functions, when appropriate. If a user query can be answered using one of the tools listed under 'Available tools', utilize them as described under 'Instructions on tool use'. If none of the listed tools are relevant to respond to the user, ignore the tools and respond naturally in plain language.

# Available tools
- The JSON below lists the functions available to you.

\`\`\`json
${JSON.stringify(tools)}
\`\`\`

# Instructions on tool use
If you choose to call one of the functions available to you, ONLY reply in the following format:
<function>{"name": function name, "parameters": dictionary of argument name and its value}</function>
Here is an example,
<function>{"name": "example_function_name", "parameters": {"example_name": "example_value"}}</function>
Reminder:
- Function calls MUST follow the specified format and use BOTH <function> and </function>.
- Required parameters MUST be specified.
- Only call one function at a time.
- When calling a function, do NOT say anything else. ONLY display the function call in the specified format.
- Do not ask for the user's permission to make the function call. Use your judgement and make the call when appropriate.
- Put the entire function call reply on one line.
`
Insert cell
import { guard } from "@mootari/inputs-submit"
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more