This commit is contained in:
2026-05-13 14:58:53 +03:00
parent bd548a9f43
commit a411c6874a
3 changed files with 161 additions and 22 deletions
+153 -14
View File
@@ -5,14 +5,53 @@ import {getOpenAITools} from "./tool-mappers";
import {TelegramStreamMessage} from "./telegram-stream-message";
import {ToolRuntimeContext} from "./tools/runtime";
import {OpenAIChatMessage} from "./openai-chat-message";
import type {ResponseCreateParamsNonStreaming, ResponseCreateParamsStreaming, ResponseInputItem, ResponseStreamEvent} from "openai/resources/responses/responses";
import type {ChatCompletionCreateParamsNonStreaming, ChatCompletionCreateParamsStreaming} from "openai/resources/chat/completions";
import type {
ResponseCreateParamsNonStreaming,
ResponseCreateParamsStreaming,
ResponseInputItem,
ResponseStreamEvent
} from "openai/resources/responses/responses";
import type {
ChatCompletionCreateParamsNonStreaming,
ChatCompletionCreateParamsStreaming
} from "openai/resources/chat/completions";
import {createGeminiOpenAiClient, createOpenAiClient} from "./ai-runtime-target";
import {aiLog, aiLogDuration, aiLogMessageIdentity, aiLogProviderTarget, aiLogToolCall} from "../logging/ai-logger";
import {AsyncIterableStream, MAX_TOOL_ROUNDS, OPENAI_IMAGE_PARTIALS, OpenAiChatCompletionResponseLike, OpenAiChatToolCallLike, OpenAiCompatibleChatMessage, OpenAiCompatibleContentPart, OpenAiResponseLike, OpenAiResponseOutputItem, RuntimeConfigSnapshot, ToolCallData, StreamingToolCallAccumulator, collectOpenAiResponseFunctionCalls, collectOpenAiResponseImages, collectOpenAiResponseText, executeToolBatch, getOpenAIResponsesToolsWithImage, openAiResponseItemCallId, safeJsonParseObject, showOpenAiGeneratedImage, ToolExecutionMemory, isRecord, roundStatus, OpenAiChatCompletionStreamChunkLike} from "./unified-ai-runner.shared";
import {
AsyncIterableStream,
collectOpenAiResponseFunctionCalls,
collectOpenAiResponseImages,
collectOpenAiResponseText,
executeToolBatch,
getOpenAIResponsesToolsWithImage,
isRecord,
MAX_TOOL_ROUNDS,
OPENAI_IMAGE_PARTIALS,
OpenAiChatCompletionResponseLike,
OpenAiChatCompletionStreamChunkLike,
OpenAiChatToolCallLike,
OpenAiCompatibleChatMessage,
OpenAiCompatibleContentPart,
openAiResponseItemCallId,
OpenAiResponseLike,
OpenAiResponseOutputItem,
roundStatus,
RuntimeConfigSnapshot,
safeJsonParseObject,
showOpenAiGeneratedImage,
StreamingToolCallAccumulator,
ToolCallData,
ToolExecutionMemory
} from "./unified-ai-runner.shared";
import {GetNoteFileResult, GetNoteFileResultSchema} from "./tools/send-note-file";
import {bot, notesDir} from "../index";
import fs from "node:fs";
import path from "node:path";
import {logError} from "../util/utils";
export async function runOpenAi(
msg: Message,
messages: OpenAIChatMessage[],
streamMessage: TelegramStreamMessage,
signal: AbortSignal,
@@ -90,6 +129,32 @@ export async function runOpenAi(
argumentsText: call.argumentsText,
}));
const toolResults = await executeToolBatch(toolCalls, streamMessage, toolContext, toolMemory);
let successGetNoteFileResult: GetNoteFileResult | undefined = undefined;
for (const toolResult of toolResults) {
try {
const raw = JSON.parse(toolResult);
const res = GetNoteFileResultSchema.safeParse(raw);
if (res.success && res.data.success) {
successGetNoteFileResult = res.data;
}
} catch {
// Not every tool result is JSON.
}
}
if (successGetNoteFileResult && "attachment" in successGetNoteFileResult) {
await bot.sendDocument({
chat_id: msg.chat.id,
reply_parameters: {
message_id: msg.message_id,
},
document: fs.createReadStream(path.join(notesDir, successGetNoteFileResult.attachment.relativePath)),
}).catch(logError);
}
const toolOutputs = calls.map((call, index) => ({
type: "function_call_output" as const,
call_id: call.callId,
@@ -228,6 +293,32 @@ export async function runOpenAi(
argumentsText: call.argumentsText,
}));
const toolResults = await executeToolBatch(toolCalls, streamMessage, toolContext, toolMemory);
let successGetNoteFileResult: GetNoteFileResult | undefined = undefined;
for (const toolResult of toolResults) {
try {
const raw = JSON.parse(toolResult);
const res = GetNoteFileResultSchema.safeParse(raw);
if (res.success && res.data.success) {
successGetNoteFileResult = res.data;
}
} catch {
// Not every tool result is JSON.
}
}
if (successGetNoteFileResult && "attachment" in successGetNoteFileResult) {
await bot.sendDocument({
chat_id: msg.chat.id,
reply_parameters: {
message_id: msg.message_id,
},
document: fs.createReadStream(path.join(notesDir, successGetNoteFileResult.attachment.relativePath)),
}).catch(logError);
}
const toolOutputs = calls.map((call, index) => ({
type: "function_call_output",
call_id: call.callId,
@@ -298,6 +389,7 @@ async function appendOpenAiChatToolResults(
}
export async function runOpenAiCompatibleChat(
msg: Message,
messages: OpenAIChatMessage[],
streamMessage: TelegramStreamMessage,
signal: AbortSignal,
@@ -356,7 +448,35 @@ export async function runOpenAiCompatibleChat(
},
})),
});
await appendOpenAiChatToolResults(chatMessages, calls, await executeToolBatch(calls, streamMessage, toolContext, toolMemory));
const toolResults = await executeToolBatch(calls, streamMessage, toolContext, toolMemory);
let successGetNoteFileResult: GetNoteFileResult | undefined = undefined;
for (const toolResult of toolResults) {
try {
const raw = JSON.parse(toolResult);
const res = GetNoteFileResultSchema.safeParse(raw);
if (res.success && res.data.success) {
successGetNoteFileResult = res.data;
}
} catch {
// Not every tool result is JSON.
}
}
if (successGetNoteFileResult && "attachment" in successGetNoteFileResult) {
await bot.sendDocument({
chat_id: msg.chat.id,
reply_parameters: {
message_id: msg.message_id,
},
document: fs.createReadStream(path.join(notesDir, successGetNoteFileResult.attachment.relativePath)),
}).catch(logError);
}
await appendOpenAiChatToolResults(chatMessages, calls, toolResults);
continue;
}
@@ -410,15 +530,34 @@ export async function runOpenAiCompatibleChat(
},
})),
});
await appendOpenAiChatToolResults(chatMessages, calls, await executeToolBatch(calls, streamMessage, toolContext, toolMemory));
const toolResults = await executeToolBatch(calls, streamMessage, toolContext, toolMemory);
let successGetNoteFileResult: GetNoteFileResult | undefined = undefined;
for (const toolResult of toolResults) {
try {
const raw = JSON.parse(toolResult);
const res = GetNoteFileResultSchema.safeParse(raw);
if (res.success && res.data.success) {
successGetNoteFileResult = res.data;
}
} catch {
// Not every tool result is JSON.
}
}
if (successGetNoteFileResult && "attachment" in successGetNoteFileResult) {
await bot.sendDocument({
chat_id: msg.chat.id,
reply_parameters: {
message_id: msg.message_id,
},
document: fs.createReadStream(path.join(notesDir, successGetNoteFileResult.attachment.relativePath)),
}).catch(logError);
}
await appendOpenAiChatToolResults(chatMessages, calls, toolResults);
}
}
export class OpenAiProviderRunner {
static run = runOpenAi;
}
export class OpenAiCompatibleProviderRunner {
static run = runOpenAiCompatibleChat;
}