shitton
This commit is contained in:
@@ -4,12 +4,12 @@ import {toolsLogger} from "./tool-logger";
|
|||||||
|
|
||||||
const logger = toolsLogger.child("market-rates");
|
const logger = toolsLogger.child("market-rates");
|
||||||
|
|
||||||
export const GET_FINANCIAL_MARKET_DATA = "get_financial_market_data";
|
export const GET_FINANCIAL_MARKET_DATA_TOOL_NAME = "get_financial_market_data";
|
||||||
|
|
||||||
export const getFinancialMarketData = {
|
export const getFinancialMarketData = {
|
||||||
type: "function",
|
type: "function",
|
||||||
function: {
|
function: {
|
||||||
name: GET_FINANCIAL_MARKET_DATA,
|
name: GET_FINANCIAL_MARKET_DATA_TOOL_NAME,
|
||||||
description:
|
description:
|
||||||
"Retrieve the latest exchange rates for supported currency, crypto, and precious metal pairs, including 24-hour change data when available. Supported pairs: USD/RUB, USD/EUR, USD/KZT, USD/UAH, USD/BYN, USD/GBP, USD/CNY, TON/USD, BTC/USD, ETH/USD, SOL/USD, and XAU/USD. Use this tool when the user asks for current rates, currency conversion, crypto prices, gold price, or recent 24-hour movement. This tool takes no parameters.",
|
"Retrieve the latest exchange rates for supported currency, crypto, and precious metal pairs, including 24-hour change data when available. Supported pairs: USD/RUB, USD/EUR, USD/KZT, USD/UAH, USD/BYN, USD/GBP, USD/CNY, TON/USD, BTC/USD, ETH/USD, SOL/USD, and XAU/USD. Use this tool when the user asks for current rates, currency conversion, crypto prices, gold price, or recent 24-hour movement. This tool takes no parameters.",
|
||||||
parameters: {
|
parameters: {
|
||||||
@@ -22,9 +22,9 @@ export const getFinancialMarketData = {
|
|||||||
|
|
||||||
export const financialMarketDataToolPrompt = [
|
export const financialMarketDataToolPrompt = [
|
||||||
"Currency rates tool rules:",
|
"Currency rates tool rules:",
|
||||||
`- Use \`${GET_FINANCIAL_MARKET_DATA}\` whenever the answer depends on current exchange rates, crypto prices, or gold price.`,
|
`- Use \`${GET_FINANCIAL_MARKET_DATA_TOOL_NAME}\` whenever the answer depends on current exchange rates, crypto prices, or gold price.`,
|
||||||
`- Use \`${GET_FINANCIAL_MARKET_DATA}\` when the user asks whether a supported asset went up or down recently.`,
|
`- Use \`${GET_FINANCIAL_MARKET_DATA_TOOL_NAME}\` when the user asks whether a supported asset went up or down recently.`,
|
||||||
`- Use \`${GET_FINANCIAL_MARKET_DATA}\` when the user asks for the 24-hour change, percentage change, or movement direction for a supported pair.`,
|
`- Use \`${GET_FINANCIAL_MARKET_DATA_TOOL_NAME}\` when the user asks for the 24-hour change, percentage change, or movement direction for a supported pair.`,
|
||||||
"- Never guess current rates, prices, or 24-hour changes. Call the tool first.",
|
"- Never guess current rates, prices, or 24-hour changes. Call the tool first.",
|
||||||
"- Do not use this tool for unsupported pairs unless the user asks about one of the supported pairs listed below.",
|
"- Do not use this tool for unsupported pairs unless the user asks about one of the supported pairs listed below.",
|
||||||
"- Do not use this tool for historical rates beyond the provided 24-hour comparison.",
|
"- Do not use this tool for historical rates beyond the provided 24-hour comparison.",
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import {AiTool} from "../tool-types";
|
import {AiTool} from "../tool-types";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import {readdir, readFile, unlink, writeFile} from "node:fs/promises";
|
import {readdir, readFile, stat, unlink, writeFile} from "node:fs/promises";
|
||||||
import {notesDir, notesRootFile} from "../../index";
|
import {notesDir, notesRootFile} from "../../index";
|
||||||
import {asNonEmptyString} from "./utils";
|
import {asNonEmptyString} from "./utils";
|
||||||
import {toolsLogger} from "./tool-logger";
|
import {toolsLogger} from "./tool-logger";
|
||||||
|
import {z} from "zod";
|
||||||
|
|
||||||
const logger = toolsLogger.child("notes");
|
const logger = toolsLogger.child("notes");
|
||||||
|
|
||||||
@@ -338,3 +339,110 @@ async function removeNoteLinkFromRoot(noteFilePath: string): Promise<void> {
|
|||||||
function escapeRegExp(value: string): string {
|
function escapeRegExp(value: string): string {
|
||||||
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NoteFileAttachment = {
|
||||||
|
type: "local_file";
|
||||||
|
fileName: string;
|
||||||
|
// filePath: string;
|
||||||
|
relativePath: string;
|
||||||
|
mimeType: "text/markdown";
|
||||||
|
sizeBytes: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetNoteFileResult =
|
||||||
|
| {
|
||||||
|
success: true;
|
||||||
|
attachment: NoteFileAttachment;
|
||||||
|
} | { success: false; error: string };
|
||||||
|
|
||||||
|
export const NoteFileAttachmentSchema = z.object({
|
||||||
|
type: z.literal("local_file"),
|
||||||
|
fileName: z.string(),
|
||||||
|
// filePath: z.string(),
|
||||||
|
relativePath: z.string(),
|
||||||
|
mimeType: z.literal("text/markdown"),
|
||||||
|
sizeBytes: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetNoteFileResultSchema = z.discriminatedUnion("success", [
|
||||||
|
z.object({
|
||||||
|
success: z.literal(true),
|
||||||
|
attachment: NoteFileAttachmentSchema,
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
success: z.literal(false),
|
||||||
|
error: z.string(),
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const sendNoteAsFileTool = {
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: "send_note_as_file",
|
||||||
|
description:
|
||||||
|
"Prepare a Markdown note file to be sent to the user as a .md attachment. Returns a local file descriptor that the host application should use to upload or send the file.",
|
||||||
|
parameters: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
fileName: {
|
||||||
|
type: "string",
|
||||||
|
description:
|
||||||
|
"The file name of the note to send. It may be provided with or without the .md extension. Must not contain forbidden or unsafe characters such as /, \\, :, *, ?, \", <, >, |, or control characters.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["fileName"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} satisfies AiTool;
|
||||||
|
|
||||||
|
export async function sendNoteAsFile(
|
||||||
|
args?: Record<string, unknown>,
|
||||||
|
): Promise<GetNoteFileResult> {
|
||||||
|
logger.debug("start", {args});
|
||||||
|
|
||||||
|
const fileName = asNonEmptyString(args?.fileName) ?? "";
|
||||||
|
if (!fileName.trim().length) {
|
||||||
|
return {success: false, error: "No file name provided"};
|
||||||
|
}
|
||||||
|
|
||||||
|
const noteFilePath = buildSafeNoteFilePath(fileName);
|
||||||
|
if (!noteFilePath) {
|
||||||
|
return {success: false, error: "Invalid or unsafe file name provided"};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Проверяем, что файл существует и действительно читается.
|
||||||
|
await readFile(noteFilePath, "utf-8");
|
||||||
|
|
||||||
|
const fileStat = await stat(noteFilePath);
|
||||||
|
if (!fileStat.isFile()) {
|
||||||
|
return {success: false, error: "Note path is not a file"};
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedFileName = path.basename(noteFilePath);
|
||||||
|
const relativePath = path.relative(path.dirname(notesRootFile), noteFilePath);
|
||||||
|
|
||||||
|
const result: GetNoteFileResult = {
|
||||||
|
success: true,
|
||||||
|
attachment: {
|
||||||
|
type: "local_file",
|
||||||
|
fileName: normalizedFileName,
|
||||||
|
// filePath: noteFilePath,
|
||||||
|
relativePath,
|
||||||
|
mimeType: "text/markdown",
|
||||||
|
sizeBytes: fileStat.size,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
logger.debug("done", {
|
||||||
|
fileName: result.attachment.fileName,
|
||||||
|
relativePath: result.attachment.relativePath,
|
||||||
|
sizeBytes: result.attachment.sizeBytes
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
|
return {success: false, error: `Failed to prepare note file: ${errorMessage}`};
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
-34
@@ -7,7 +7,7 @@ import {ToolHandler} from "./types";
|
|||||||
import {getWeather, getWeatherTool} from "./weather";
|
import {getWeather, getWeatherTool} from "./weather";
|
||||||
import {
|
import {
|
||||||
financialMarketDataToolPrompt,
|
financialMarketDataToolPrompt,
|
||||||
GET_FINANCIAL_MARKET_DATA,
|
GET_FINANCIAL_MARKET_DATA_TOOL_NAME,
|
||||||
getFinancialMarketData,
|
getFinancialMarketData,
|
||||||
getMarketRates
|
getMarketRates
|
||||||
} from "./market-rates";
|
} from "./market-rates";
|
||||||
@@ -38,16 +38,30 @@ import {
|
|||||||
getNoteContentTool,
|
getNoteContentTool,
|
||||||
listNotes,
|
listNotes,
|
||||||
listNotesTool,
|
listNotesTool,
|
||||||
|
sendNoteAsFile,
|
||||||
|
sendNoteAsFileTool,
|
||||||
updateNoteContent,
|
updateNoteContent,
|
||||||
updateNoteContentTool
|
updateNoteContentTool
|
||||||
} from "./list-notes";
|
} from "./notes";
|
||||||
import {sendNoteAsFileTool, sendNoteAsFile} from "./send-note-as-file";
|
|
||||||
import {searchNotes, searchNotesTool} from "./search-notes";
|
import {searchNotes, searchNotesTool} from "./search-notes";
|
||||||
|
|
||||||
export const getTools = () => {
|
export const defaultFileTools: AiTool[] = [
|
||||||
const tools: AiTool[] = [
|
|
||||||
getCurrentDateTimeTool,
|
getCurrentDateTimeTool,
|
||||||
getFinancialMarketData,
|
getFinancialMarketData,
|
||||||
|
]
|
||||||
|
|
||||||
|
export const fileSystemTools: AiTool[] = [
|
||||||
|
readFileTool,
|
||||||
|
listDirectoryTool,
|
||||||
|
createFileTool,
|
||||||
|
createDirectoryTool,
|
||||||
|
updateFileTool,
|
||||||
|
renamePathTool,
|
||||||
|
copyPathTool,
|
||||||
|
deletePathTool,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const notesFileTools: AiTool[] = [
|
||||||
createNoteTool,
|
createNoteTool,
|
||||||
listNotesTool,
|
listNotesTool,
|
||||||
getNoteContentTool,
|
getNoteContentTool,
|
||||||
@@ -55,16 +69,14 @@ export const getTools = () => {
|
|||||||
deleteNoteTool,
|
deleteNoteTool,
|
||||||
sendNoteAsFileTool,
|
sendNoteAsFileTool,
|
||||||
searchNotesTool
|
searchNotesTool
|
||||||
|
]
|
||||||
|
|
||||||
|
export const getTools = (forCreator?: boolean) => {
|
||||||
|
const tools: AiTool[] = [
|
||||||
|
...defaultFileTools,
|
||||||
|
...notesFileTools
|
||||||
];
|
];
|
||||||
|
|
||||||
if (Environment.ENABLE_PYTHON_INTERPRETER) {
|
|
||||||
tools.push(pythonInterpreterTool);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Environment.ENABLE_UNSAFE_EVAL) {
|
|
||||||
tools.push(shellExecuteTool);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Environment.BRAVE_SEARCH_API_KEY) {
|
if (Environment.BRAVE_SEARCH_API_KEY) {
|
||||||
tools.push(braveSearchTool);
|
tools.push(braveSearchTool);
|
||||||
}
|
}
|
||||||
@@ -73,30 +85,21 @@ export const getTools = () => {
|
|||||||
tools.push(getWeatherTool);
|
tools.push(getWeatherTool);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Environment.FILE_TOOLS_ROOT_DIR && Environment.ENABLE_FS_TOOLS) {
|
if (forCreator) {
|
||||||
tools.push(
|
if (Environment.ENABLE_PYTHON_INTERPRETER) {
|
||||||
readFileTool,
|
tools.push(pythonInterpreterTool);
|
||||||
listDirectoryTool,
|
|
||||||
createFileTool,
|
|
||||||
createDirectoryTool,
|
|
||||||
updateFileTool,
|
|
||||||
renamePathTool,
|
|
||||||
copyPathTool,
|
|
||||||
deletePathTool,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Environment.ENABLE_UNSAFE_EVAL) {
|
||||||
|
tools.push(shellExecuteTool);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Environment.FILE_TOOLS_ROOT_DIR && Environment.ENABLE_FS_TOOLS) {
|
||||||
|
tools.push(...fileSystemTools);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return tools;
|
return tools;
|
||||||
// return [
|
|
||||||
// createNoteTool,
|
|
||||||
// listNotesTool,
|
|
||||||
// getNoteContentTool,
|
|
||||||
// updateNoteContentTool,
|
|
||||||
// deleteNoteTool,
|
|
||||||
// getNoteFileTool,
|
|
||||||
// searchNotesTool
|
|
||||||
// ];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getToolHandlers = () => {
|
export const getToolHandlers = () => {
|
||||||
@@ -162,7 +165,7 @@ export function getToolPrompts(toolNames: string[]): string[] {
|
|||||||
|
|
||||||
for (const toolName of toolNames) {
|
for (const toolName of toolNames) {
|
||||||
switch (toolName) {
|
switch (toolName) {
|
||||||
case GET_FINANCIAL_MARKET_DATA:
|
case GET_FINANCIAL_MARKET_DATA_TOOL_NAME:
|
||||||
prompts.push(financialMarketDataToolPrompt);
|
prompts.push(financialMarketDataToolPrompt);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
import {AiTool} from "../tool-types";
|
|
||||||
import path from "node:path";
|
|
||||||
import {readFile, stat} from "node:fs/promises";
|
|
||||||
import {notesRootFile} from "../../index";
|
|
||||||
import {asNonEmptyString} from "./utils";
|
|
||||||
import {buildSafeNoteFilePath} from "./list-notes";
|
|
||||||
import z from "zod";
|
|
||||||
import {toolsLogger} from "./tool-logger";
|
|
||||||
|
|
||||||
const logger = toolsLogger.child("get-note-file");
|
|
||||||
|
|
||||||
export type NoteFileAttachment = {
|
|
||||||
type: "local_file";
|
|
||||||
fileName: string;
|
|
||||||
// filePath: string;
|
|
||||||
relativePath: string;
|
|
||||||
mimeType: "text/markdown";
|
|
||||||
sizeBytes: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetNoteFileResult =
|
|
||||||
| {
|
|
||||||
success: true;
|
|
||||||
attachment: NoteFileAttachment;
|
|
||||||
} | { success: false; error: string };
|
|
||||||
|
|
||||||
export const NoteFileAttachmentSchema = z.object({
|
|
||||||
type: z.literal("local_file"),
|
|
||||||
fileName: z.string(),
|
|
||||||
// filePath: z.string(),
|
|
||||||
relativePath: z.string(),
|
|
||||||
mimeType: z.literal("text/markdown"),
|
|
||||||
sizeBytes: z.number(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const GetNoteFileResultSchema = z.discriminatedUnion("success", [
|
|
||||||
z.object({
|
|
||||||
success: z.literal(true),
|
|
||||||
attachment: NoteFileAttachmentSchema,
|
|
||||||
}),
|
|
||||||
z.object({
|
|
||||||
success: z.literal(false),
|
|
||||||
error: z.string(),
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
export const sendNoteAsFileTool = {
|
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "send_note_as_file",
|
|
||||||
description:
|
|
||||||
"Prepare a Markdown note file to be sent to the user as a .md attachment. Returns a local file descriptor that the host application should use to upload or send the file.",
|
|
||||||
parameters: {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
fileName: {
|
|
||||||
type: "string",
|
|
||||||
description:
|
|
||||||
"The file name of the note to send. It may be provided with or without the .md extension. Must not contain forbidden or unsafe characters such as /, \\, :, *, ?, \", <, >, |, or control characters.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: ["fileName"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} satisfies AiTool;
|
|
||||||
|
|
||||||
export async function sendNoteAsFile(
|
|
||||||
args?: Record<string, unknown>,
|
|
||||||
): Promise<GetNoteFileResult> {
|
|
||||||
logger.debug("start", {args});
|
|
||||||
|
|
||||||
const fileName = asNonEmptyString(args?.fileName) ?? "";
|
|
||||||
if (!fileName.trim().length) {
|
|
||||||
return {success: false, error: "No file name provided"};
|
|
||||||
}
|
|
||||||
|
|
||||||
const noteFilePath = buildSafeNoteFilePath(fileName);
|
|
||||||
if (!noteFilePath) {
|
|
||||||
return {success: false, error: "Invalid or unsafe file name provided"};
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Проверяем, что файл существует и действительно читается.
|
|
||||||
await readFile(noteFilePath, "utf-8");
|
|
||||||
|
|
||||||
const fileStat = await stat(noteFilePath);
|
|
||||||
if (!fileStat.isFile()) {
|
|
||||||
return {success: false, error: "Note path is not a file"};
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalizedFileName = path.basename(noteFilePath);
|
|
||||||
const relativePath = path.relative(path.dirname(notesRootFile), noteFilePath);
|
|
||||||
|
|
||||||
const result: GetNoteFileResult = {
|
|
||||||
success: true,
|
|
||||||
attachment: {
|
|
||||||
type: "local_file",
|
|
||||||
fileName: normalizedFileName,
|
|
||||||
// filePath: noteFilePath,
|
|
||||||
relativePath,
|
|
||||||
mimeType: "text/markdown",
|
|
||||||
sizeBytes: fileStat.size,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
logger.debug("done", {fileName: result.attachment.fileName, relativePath: result.attachment.relativePath, sizeBytes: result.attachment.sizeBytes});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} catch (error) {
|
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
||||||
return {success: false, error: `Failed to prepare note file: ${errorMessage}`};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,7 +16,6 @@ import {getFinancialMarketData} from "./tools/market-rates";
|
|||||||
import {getWeatherTool} from "./tools/weather";
|
import {getWeatherTool} from "./tools/weather";
|
||||||
import {loadOllamaModel, unloadAllOllamaModels} from "./tools/utils";
|
import {loadOllamaModel, unloadAllOllamaModels} from "./tools/utils";
|
||||||
import {createOllamaClient} from "./ai-runtime-target";
|
import {createOllamaClient} from "./ai-runtime-target";
|
||||||
import {GetNoteFileResult, GetNoteFileResultSchema, sendNoteAsFileTool} from "./tools/send-note-as-file";
|
|
||||||
import {aiLog, aiLogDuration, aiLogMessageIdentity, aiLogProviderTarget, aiLogToolCall} from "../logging/ai-logger";
|
import {aiLog, aiLogDuration, aiLogMessageIdentity, aiLogProviderTarget, aiLogToolCall} from "../logging/ai-logger";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -42,7 +41,15 @@ import {
|
|||||||
import {latestUserTextFromOllamaMessages, OllamaToolRanker} from "./unified-ai-runner.tool-ranker";
|
import {latestUserTextFromOllamaMessages, OllamaToolRanker} from "./unified-ai-runner.tool-ranker";
|
||||||
import {getToolPrompts} from "./tools/registry";
|
import {getToolPrompts} from "./tools/registry";
|
||||||
import {createNoteTool} from "./tools/create-note";
|
import {createNoteTool} from "./tools/create-note";
|
||||||
import {deleteNoteTool, getNoteContentTool, listNotesTool, updateNoteContentTool} from "./tools/list-notes";
|
import {
|
||||||
|
deleteNoteTool,
|
||||||
|
getNoteContentTool,
|
||||||
|
GetNoteFileResult,
|
||||||
|
GetNoteFileResultSchema,
|
||||||
|
listNotesTool,
|
||||||
|
sendNoteAsFileTool,
|
||||||
|
updateNoteContentTool
|
||||||
|
} from "./tools/notes";
|
||||||
import {searchNotesTool} from "./tools/search-notes";
|
import {searchNotesTool} from "./tools/search-notes";
|
||||||
|
|
||||||
export async function runOllama(
|
export async function runOllama(
|
||||||
|
|||||||
Reference in New Issue
Block a user