Skip to content

Commit

Permalink
Bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
msveshnikov committed May 1, 2024
1 parent 493fb64 commit a92ad4b
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 287 deletions.
2 changes: 1 addition & 1 deletion python-scripts/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def execute_with_timeout():
try:
with contextlib.redirect_stdout(output_stream), contextlib.redirect_stderr(output_stream):
signal.signal(signal.SIGALRM, self.handle_timeout)
signal.alarm(180) # Set a 3-minute timeout
signal.alarm(60) # Set a 1-minute timeout
execute_with_timeout()
signal.alarm(0) # Cancel the timeout
output = output_stream.getvalue()
Expand Down
87 changes: 41 additions & 46 deletions server/claude.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import Anthropic from "@anthropic-ai/sdk";
import dotenv from "dotenv";
import TelegramBot from "node-telegram-bot-api";
import sharp from "sharp";
import { handleToolCall, tools } from "./tools.js";
dotenv.config({ override: true });

export const bot = new TelegramBot(process.env.TELEGRAM_KEY);
let anthropic;

const resizeImage = async (imageBase64, maxSize = 2 * 1024 * 1024) => {
const image = sharp(Buffer.from(imageBase64, "base64"));
const metadata = await image.metadata();
Expand All @@ -27,57 +23,56 @@ const resizeImage = async (imageBase64, maxSize = 2 * 1024 * 1024) => {
}
};

async function processToolResult(data, temperature, messages, userId, model, webTools) {
console.log("processToolResult", data, messages);

const toolUses = data.content.filter((block) => block.type === "tool_use");
if (!toolUses.length) {
return data?.content?.[0]?.text;
}

const toolResults = await Promise.all(
toolUses.map(async (toolUse) => {
const toolResult = await handleToolCall(toolUse.name, toolUse.input, userId);
return { tool_use_id: toolUse.id, content: toolResult };
})
);

const newMessages = [
...messages,
{ role: "assistant", content: data.content.filter((c) => c.type !== "text" || c.text) },
{
role: "user",
content: toolResults.map((toolResult) => ({
type: "tool_result",
...toolResult,
})),
},
];

const newData = await anthropic.beta.tools.messages.create({
model,
max_tokens: 4096,
temperature: temperature || 0.5,
tools: webTools ? tools : [],
messages: newMessages,
});
if (newData.stop_reason === "tool_use") {
return await processToolResult(newData, temperature, newMessages, userId, model, webTools);
} else {
return newData?.content?.[0]?.text;
}
}

export const getTextClaude = async (prompt, temperature, imageBase64, fileType, userId, model, apiKey, webTools) => {
let anthropic;
if (apiKey) {
anthropic = new Anthropic({ apiKey });
} else {
anthropic = new Anthropic({ apiKey: process.env.CLAUDE_KEY });
if (model?.includes("opus") || model?.includes("sonnet") || !model) {
if (model?.includes("opus") || !model) {
model = "claude-3-haiku-20240307";
}
}

async function processToolResult(data, temperature, messages, userId, model, webTools) {
const toolUses = data.content.filter((block) => block.type === "tool_use");
if (!toolUses.length) {
return data?.content?.[0]?.text;
}

const toolResults = await Promise.all(
toolUses.map(async (toolUse) => {
const toolResult = await handleToolCall(toolUse.name, toolUse.input, userId);
return { tool_use_id: toolUse.id, content: toolResult };
})
);

const newMessages = [
...messages,
{ role: "assistant", content: data.content.filter((c) => c.type !== "text" || c.text) },
{
role: "user",
content: toolResults.map((toolResult) => ({
type: "tool_result",
...toolResult,
})),
},
];

const newData = await anthropic.beta.tools.messages.create({
model,
max_tokens: 4096,
temperature: temperature || 0.5,
tools: webTools ? tools : [],
messages: newMessages,
});
if (newData.stop_reason === "tool_use") {
return await processToolResult(newData, temperature, newMessages, userId, model, webTools);
} else {
return newData?.content?.[0]?.text;
}
}

const messages = [
{
role: "user",
Expand Down
83 changes: 49 additions & 34 deletions server/tools.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import nodemailer from "nodemailer";
import fs from "fs";
import path from "path";
import { fetchPageContent, fetchSearchResults, googleNews } from "./search.js";
import { User } from "./model/User.js";
import { scheduleAction } from "./scheduler.js";
import { toolsUsed } from "./index.js";
import { contentFolder, toolsUsed } from "./index.js";
import { summarizeYouTubeVideo } from "./youtube.js";
import { bot } from "./claude.js";
import TelegramBot from "node-telegram-bot-api";

const bot = new TelegramBot(process.env.TELEGRAM_KEY);
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.EMAIL,
pass: process.env.EMAIL_PASSWORD,
},
});

export const tools = [
{
Expand Down Expand Up @@ -250,37 +261,38 @@ export const handleToolCall = async (name, args, userId) => {
};

export async function getWeather(location) {
const weatherUrl = `https://api.openweathermap.org/data/2.5/weather?q=${location}&appid=${process.env.OPENWEATHER_API_KEY}`;
const forecastUrl = `https://api.openweathermap.org/data/2.5/forecast?q=${location}&appid=${process.env.OPENWEATHER_API_KEY}`;

const [weatherResponse, forecastResponse] = await Promise.all([fetch(weatherUrl), fetch(forecastUrl)]);

const [weatherData, forecastData] = await Promise.all([weatherResponse.json(), forecastResponse.json()]);

const { name, weather, main } = weatherData;
const { list } = forecastData;
try {
const weatherUrl = `https://api.openweathermap.org/data/2.5/weather?q=${location}&appid=${process.env.OPENWEATHER_API_KEY}`;
const forecastUrl = `https://api.openweathermap.org/data/2.5/forecast?q=${location}&appid=${process.env.OPENWEATHER_API_KEY}`;
const [weatherResponse, forecastResponse] = await Promise.all([fetch(weatherUrl), fetch(forecastUrl)]);
const [weatherData, forecastData] = await Promise.all([weatherResponse.json(), forecastResponse.json()]);
const { name, weather, main } = weatherData;
const { list } = forecastData;

const currentWeather = `In ${name}, the weather is ${weather?.[0]?.description} with a temperature of ${Math.round(
main?.temp - 273
)}°C`;
const currentWeather = `In ${name}, the weather is ${
weather?.[0]?.description
} with a temperature of ${Math.round(main?.temp - 273)}°C`;

const fiveDayForecast = list
?.filter((_, index) => index % 8 === 0) // Get one forecast per day
?.map((item) => {
const date = new Date(item.dt * 1000).toLocaleDateString();
const temperature = Math.round(item.main.temp - 273);
const description = item.weather[0].description;
return `On ${date}, the weather will be ${description} with a temperature of ${temperature}°C`;
})
?.join("\n");
const fiveDayForecast = list
?.filter((_, index) => index % 8 === 4) //noon
?.map((item) => {
const date = new Date(item.dt * 1000).toLocaleDateString();
const temperature = Math.round(item.main.temp - 273);
const description = item.weather[0].description;
return `On ${date}, the weather will be ${description} with a temperature of ${temperature}°C`;
})
?.join("\n");

return `${currentWeather}\n\nFive-day forecast:\n${fiveDayForecast}`;
return `${currentWeather}\n\nFive-day forecast:\n${fiveDayForecast}`;
} catch (error) {
console.error("Error fetching weather:", error);
return "Error fetching weather:" + error.message;
}
}

export async function getStockPrice(ticker) {
const apiUrl = `https://yfapi.net/v8/finance/chart/${ticker}?range=1wk&interval=1d&lang=en-US&region=US&includePrePost=false&corsDomain=finance.yahoo.com`;

try {
const apiUrl = `https://yfapi.net/v8/finance/chart/${ticker}?range=1wk&interval=1d&lang=en-US&region=US&includePrePost=false&corsDomain=finance.yahoo.com`;
const response = await fetch(apiUrl, {
headers: {
"X-API-KEY": process.env.YAHOO_FINANCE_API_KEY,
Expand Down Expand Up @@ -330,13 +342,6 @@ export async function sendTelegramMessage(chatId, message) {
return "Error sending Telegram message:" + error.message;
}
}
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.EMAIL,
pass: process.env.EMAIL_PASSWORD,
},
});

export async function sendEmail(to, subject, content, userId) {
let recipient;
Expand Down Expand Up @@ -378,7 +383,17 @@ export async function executePython(code) {
const data = await response.text();
if (response.ok) {
const jsonData = JSON.parse(data);
return jsonData.output;
let output = jsonData.output;
const newFiles = jsonData.new_files;
for (const [filePath, base64Content] of Object.entries(newFiles)) {
const fileName = path.basename(filePath);
const fileContent = Buffer.from(base64Content, "base64");
const fileSavePath = path.join(contentFolder, fileName);
fs.writeFileSync(fileSavePath, fileContent);
const hyperlink = `[${fileName}](/api/get?file=${encodeURIComponent(fileName)})`;
output += `\n${hyperlink}`;
}
return output;
} else {
return data;
}
Expand Down
Loading

0 comments on commit a92ad4b

Please sign in to comment.