mirror of
https://github.com/zadam/trilium.git
synced 2025-10-20 15:19:01 +02:00
refactor: proper websocket message types
This commit is contained in:
parent
998688573d
commit
4cd0702cbb
@ -116,7 +116,7 @@ export type CommandMappings = {
|
||||
openedFileUpdated: CommandData & {
|
||||
entityType: string;
|
||||
entityId: string;
|
||||
lastModifiedMs: number;
|
||||
lastModifiedMs?: number;
|
||||
filePath: string;
|
||||
};
|
||||
focusAndSelectTitle: CommandData & {
|
||||
|
@ -210,7 +210,7 @@ function makeToast(id: string, message: string): ToastOptions {
|
||||
}
|
||||
|
||||
ws.subscribeToMessages(async (message) => {
|
||||
if (message.taskType !== "deleteNotes") {
|
||||
if (!("taskType" in message) || message.taskType !== "deleteNotes") {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -228,7 +228,7 @@ ws.subscribeToMessages(async (message) => {
|
||||
});
|
||||
|
||||
ws.subscribeToMessages(async (message) => {
|
||||
if (message.taskType !== "undeleteNotes") {
|
||||
if (!("taskType" in message) || message.taskType !== "undeleteNotes") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,8 @@
|
||||
import ws from "./ws.js";
|
||||
import appContext from "../components/app_context.js";
|
||||
import { OpenedFileUpdateStatus } from "@triliumnext/commons";
|
||||
|
||||
// TODO: Deduplicate
|
||||
interface Message {
|
||||
type: string;
|
||||
entityType: string;
|
||||
entityId: string;
|
||||
lastModifiedMs: number;
|
||||
filePath: string;
|
||||
}
|
||||
|
||||
const fileModificationStatus: Record<string, Record<string, Message>> = {
|
||||
const fileModificationStatus: Record<string, Record<string, OpenedFileUpdateStatus>> = {
|
||||
notes: {},
|
||||
attachments: {}
|
||||
};
|
||||
@ -39,7 +31,7 @@ function ignoreModification(entityType: string, entityId: string) {
|
||||
delete fileModificationStatus[entityType][entityId];
|
||||
}
|
||||
|
||||
ws.subscribeToMessages(async (message: Message) => {
|
||||
ws.subscribeToMessages(async message => {
|
||||
if (message.type !== "openedFileUpdated") {
|
||||
return;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import ws from "./ws.js";
|
||||
import utils from "./utils.js";
|
||||
import appContext from "../components/app_context.js";
|
||||
import { t } from "./i18n.js";
|
||||
import { WebSocketMessage } from "@triliumnext/commons";
|
||||
|
||||
type BooleanLike = boolean | "true" | "false";
|
||||
|
||||
@ -66,7 +67,7 @@ function makeToast(id: string, message: string): ToastOptions {
|
||||
}
|
||||
|
||||
ws.subscribeToMessages(async (message) => {
|
||||
if (message.taskType !== "importNotes") {
|
||||
if (!("taskType" in message) || message.taskType !== "importNotes") {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -81,14 +82,14 @@ ws.subscribeToMessages(async (message) => {
|
||||
|
||||
toastService.showPersistent(toast);
|
||||
|
||||
if (message.result.importedNoteId) {
|
||||
if (typeof message.result === "object" && message.result.importedNoteId) {
|
||||
await appContext.tabManager.getActiveContext()?.setNote(message.result.importedNoteId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ws.subscribeToMessages(async (message) => {
|
||||
if (message.taskType !== "importAttachments") {
|
||||
ws.subscribeToMessages(async (message: WebSocketMessage) => {
|
||||
if (!("taskType" in message) || message.taskType !== "importAttachments") {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -103,7 +104,7 @@ ws.subscribeToMessages(async (message) => {
|
||||
|
||||
toastService.showPersistent(toast);
|
||||
|
||||
if (message.result.parentNoteId) {
|
||||
if (typeof message.result === "object" && message.result.parentNoteId) {
|
||||
await appContext.tabManager.getActiveContext()?.setNote(message.result.importedNoteId, {
|
||||
viewScope: {
|
||||
viewMode: "attachments"
|
||||
|
@ -107,7 +107,7 @@ function makeToast(message: Message, title: string, text: string): ToastOptions
|
||||
}
|
||||
|
||||
ws.subscribeToMessages(async (message) => {
|
||||
if (message.taskType !== "protectNotes") {
|
||||
if (!("taskType" in message) || message.taskType !== "protectNotes") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,9 @@ import frocaUpdater from "./froca_updater.js";
|
||||
import appContext from "../components/app_context.js";
|
||||
import { t } from "./i18n.js";
|
||||
import type { EntityChange } from "../server_types.js";
|
||||
import { WebSocketMessage } from "@triliumnext/commons";
|
||||
|
||||
type MessageHandler = (message: any) => void;
|
||||
type MessageHandler = (message: WebSocketMessage) => void;
|
||||
const messageHandlers: MessageHandler[] = [];
|
||||
|
||||
let ws: WebSocket;
|
||||
|
@ -140,7 +140,7 @@ ws.subscribeToMessages(async (message) => {
|
||||
};
|
||||
}
|
||||
|
||||
if (message.taskType !== "export") {
|
||||
if (!("taskType" in message) || message.taskType !== "export") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import options from "../services/options.js";
|
||||
import syncService from "../services/sync.js";
|
||||
import { escapeQuotes } from "../services/utils.js";
|
||||
import { Tooltip } from "bootstrap";
|
||||
import { WebSocketMessage } from "@triliumnext/commons";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<div class="sync-status-widget launcher-button">
|
||||
@ -117,8 +118,7 @@ export default class SyncStatusWidget extends BasicWidget {
|
||||
this.$widget.find(`.sync-status-${className}`).show();
|
||||
}
|
||||
|
||||
// TriliumNextTODO: Use Type Message from "services/ws.ts"
|
||||
processMessage(message: { type: string; lastSyncedPush: number; data: { lastSyncedPush: number } }) {
|
||||
processMessage(message: WebSocketMessage) {
|
||||
if (message.type === "sync-pull-in-progress") {
|
||||
this.syncState = "in-progress";
|
||||
this.lastSyncedPush = message.lastSyncedPush;
|
||||
|
@ -73,14 +73,14 @@ export default class WatchedFileUpdateStatusWidget extends NoteContextAwareWidge
|
||||
|
||||
async refreshWithNote(note: FNote) {
|
||||
const { entityType, entityId } = this.getEntity();
|
||||
if (!entityType || !entityId) {
|
||||
return;
|
||||
}
|
||||
if (!entityType || !entityId) return;
|
||||
const status = fileWatcher.getFileModificationStatus(entityType, entityId);
|
||||
|
||||
this.$filePath.text(status.filePath);
|
||||
if (status.lastModifiedMs) {
|
||||
this.$fileLastModified.text(dayjs.unix(status.lastModifiedMs / 1000).format("HH:mm:ss"));
|
||||
}
|
||||
}
|
||||
|
||||
getEntity() {
|
||||
if (!this.noteContext) {
|
||||
|
@ -1,18 +1,9 @@
|
||||
import type { Request, Response } from "express";
|
||||
import log from "../../services/log.js";
|
||||
import options from "../../services/options.js";
|
||||
|
||||
import restChatService from "../../services/llm/rest_chat_service.js";
|
||||
import chatStorageService from '../../services/llm/chat_storage_service.js';
|
||||
|
||||
// Define basic interfaces
|
||||
interface ChatMessage {
|
||||
role: 'user' | 'assistant' | 'system';
|
||||
content: string;
|
||||
timestamp?: Date;
|
||||
}
|
||||
|
||||
|
||||
import { WebSocketMessage } from "@triliumnext/commons";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
@ -590,7 +581,7 @@ async function handleStreamingProcess(
|
||||
chatNoteId: chatNoteId
|
||||
},
|
||||
streamCallback: (data, done, rawChunk) => {
|
||||
const message = {
|
||||
const message: WebSocketMessage = {
|
||||
type: 'llm-stream' as const,
|
||||
chatNoteId: chatNoteId,
|
||||
done: done
|
||||
|
@ -2,8 +2,7 @@
|
||||
|
||||
import mimeTypes from "mime-types";
|
||||
import path from "path";
|
||||
import type { TaskData } from "../task_context_interface.js";
|
||||
import type { NoteType } from "@triliumnext/commons";
|
||||
import type { NoteType, TaskData } from "@triliumnext/commons";
|
||||
|
||||
const CODE_MIME_TYPES = new Set([
|
||||
"application/json",
|
||||
|
@ -4,7 +4,6 @@
|
||||
import log from "../../../log.js";
|
||||
import type { Response } from "express";
|
||||
import type { StreamChunk } from "../../ai_interface.js";
|
||||
import type { LLMStreamMessage } from "../../interfaces/chat_ws_messages.js";
|
||||
import type { ChatSession } from "../../interfaces/chat_session.js";
|
||||
|
||||
/**
|
||||
@ -46,7 +45,7 @@ export class StreamHandler {
|
||||
type: 'llm-stream',
|
||||
chatNoteId,
|
||||
thinking: 'Preparing response...'
|
||||
} as LLMStreamMessage);
|
||||
});
|
||||
|
||||
try {
|
||||
// Import the tool handler
|
||||
@ -66,7 +65,7 @@ export class StreamHandler {
|
||||
type: 'llm-stream',
|
||||
chatNoteId,
|
||||
thinking: 'Analyzing tools needed for this request...'
|
||||
} as LLMStreamMessage);
|
||||
});
|
||||
|
||||
try {
|
||||
// Execute the tools
|
||||
@ -82,7 +81,7 @@ export class StreamHandler {
|
||||
tool: toolResult.name,
|
||||
result: toolResult.content.substring(0, 100) + (toolResult.content.length > 100 ? '...' : '')
|
||||
}
|
||||
} as LLMStreamMessage);
|
||||
});
|
||||
}
|
||||
|
||||
// Make follow-up request with tool results
|
||||
@ -123,7 +122,7 @@ export class StreamHandler {
|
||||
chatNoteId,
|
||||
error: `Error executing tools: ${toolError instanceof Error ? toolError.message : 'Unknown error'}`,
|
||||
done: true
|
||||
} as LLMStreamMessage);
|
||||
});
|
||||
}
|
||||
} else if (response.stream) {
|
||||
// Handle standard streaming through the stream() method
|
||||
@ -152,7 +151,7 @@ export class StreamHandler {
|
||||
chatNoteId,
|
||||
content: messageContent,
|
||||
done: true
|
||||
} as LLMStreamMessage);
|
||||
});
|
||||
|
||||
log.info(`Complete response sent`);
|
||||
|
||||
@ -174,14 +173,14 @@ export class StreamHandler {
|
||||
type: 'llm-stream',
|
||||
chatNoteId,
|
||||
error: `Error generating response: ${streamingError instanceof Error ? streamingError.message : 'Unknown error'}`
|
||||
} as LLMStreamMessage);
|
||||
});
|
||||
|
||||
// Signal completion
|
||||
wsService.sendMessageToAllClients({
|
||||
type: 'llm-stream',
|
||||
chatNoteId,
|
||||
done: true
|
||||
} as LLMStreamMessage);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,7 +217,7 @@ export class StreamHandler {
|
||||
done: !!chunk.done, // Include done flag with each chunk
|
||||
// Include any raw data from the provider that might contain thinking/tool info
|
||||
...(chunk.raw ? { raw: chunk.raw } : {})
|
||||
} as LLMStreamMessage);
|
||||
});
|
||||
|
||||
// Log the first chunk (useful for debugging)
|
||||
if (messageContent.length === chunk.text.length) {
|
||||
@ -232,7 +231,7 @@ export class StreamHandler {
|
||||
type: 'llm-stream',
|
||||
chatNoteId,
|
||||
thinking: chunk.raw.thinking
|
||||
} as LLMStreamMessage);
|
||||
});
|
||||
}
|
||||
|
||||
// If the provider indicates tool execution, relay that
|
||||
@ -241,7 +240,7 @@ export class StreamHandler {
|
||||
type: 'llm-stream',
|
||||
chatNoteId,
|
||||
toolExecution: chunk.raw.toolExecution
|
||||
} as LLMStreamMessage);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle direct tool_calls in the response (for OpenAI)
|
||||
@ -252,7 +251,7 @@ export class StreamHandler {
|
||||
wsService.sendMessageToAllClients({
|
||||
type: 'tool_execution_start',
|
||||
chatNoteId
|
||||
} as LLMStreamMessage);
|
||||
});
|
||||
|
||||
// Process each tool call
|
||||
for (const toolCall of chunk.tool_calls) {
|
||||
@ -277,7 +276,7 @@ export class StreamHandler {
|
||||
toolCallId: toolCall.id,
|
||||
args: args
|
||||
}
|
||||
} as LLMStreamMessage);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,7 +336,7 @@ export class StreamHandler {
|
||||
type: 'llm-stream',
|
||||
chatNoteId,
|
||||
done: true
|
||||
} as LLMStreamMessage);
|
||||
});
|
||||
}
|
||||
|
||||
// Store the full response in the session
|
||||
@ -360,7 +359,7 @@ export class StreamHandler {
|
||||
chatNoteId,
|
||||
error: `Error during streaming: ${streamError instanceof Error ? streamError.message : 'Unknown error'}`,
|
||||
done: true
|
||||
} as LLMStreamMessage);
|
||||
});
|
||||
|
||||
throw streamError;
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import { ToolHandler } from './handlers/tool_handler.js';
|
||||
import { StreamHandler } from './handlers/stream_handler.js';
|
||||
import * as messageFormatter from './utils/message_formatter.js';
|
||||
import type { ChatSession, ChatMessage, NoteSource } from '../interfaces/chat_session.js';
|
||||
import type { LLMStreamMessage } from '../interfaces/chat_ws_messages.js';
|
||||
|
||||
// Export components
|
||||
export {
|
||||
@ -22,6 +21,5 @@ export {
|
||||
export type {
|
||||
ChatSession,
|
||||
ChatMessage,
|
||||
NoteSource,
|
||||
LLMStreamMessage
|
||||
NoteSource
|
||||
};
|
||||
|
@ -4,18 +4,15 @@
|
||||
*/
|
||||
import log from "../../log.js";
|
||||
import type { Request, Response } from "express";
|
||||
import type { Message, ChatCompletionOptions } from "../ai_interface.js";
|
||||
import type { Message } from "../ai_interface.js";
|
||||
import aiServiceManager from "../ai_service_manager.js";
|
||||
import { ChatPipeline } from "../pipeline/chat_pipeline.js";
|
||||
import type { ChatPipelineInput } from "../pipeline/interfaces.js";
|
||||
import options from "../../options.js";
|
||||
import { ToolHandler } from "./handlers/tool_handler.js";
|
||||
import type { LLMStreamMessage } from "../interfaces/chat_ws_messages.js";
|
||||
import chatStorageService from '../chat_storage_service.js';
|
||||
import {
|
||||
isAIEnabled,
|
||||
getSelectedModelConfig,
|
||||
} from '../config/configuration_helpers.js';
|
||||
import { getSelectedModelConfig } from '../config/configuration_helpers.js';
|
||||
import { WebSocketMessage } from "@triliumnext/commons";
|
||||
|
||||
/**
|
||||
* Simplified service to handle chat API interactions
|
||||
@ -204,7 +201,7 @@ class RestChatService {
|
||||
accumulatedContentRef: { value: string },
|
||||
chat: { id: string; messages: Message[]; title: string }
|
||||
) {
|
||||
const message: LLMStreamMessage = {
|
||||
const message: WebSocketMessage = {
|
||||
type: 'llm-stream',
|
||||
chatNoteId: chatNoteId,
|
||||
done: done
|
||||
|
@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Interfaces for WebSocket LLM streaming messages
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for WebSocket LLM streaming messages
|
||||
*/
|
||||
export interface LLMStreamMessage {
|
||||
type: 'llm-stream' | 'tool_execution_start' | 'tool_result' | 'tool_execution_error' | 'tool_completion_processing';
|
||||
chatNoteId: string;
|
||||
content?: string;
|
||||
thinking?: string;
|
||||
toolExecution?: {
|
||||
action?: string;
|
||||
tool?: string;
|
||||
toolCallId?: string;
|
||||
result?: string | Record<string, any>;
|
||||
error?: string;
|
||||
args?: Record<string, unknown>;
|
||||
};
|
||||
done?: boolean;
|
||||
error?: string;
|
||||
raw?: unknown;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
import type { TaskData } from "./task_context_interface.js";
|
||||
import type { TaskData } from "@triliumnext/commons";
|
||||
import ws from "./ws.js";
|
||||
|
||||
// taskId => TaskContext
|
||||
@ -61,7 +61,7 @@ class TaskContext {
|
||||
taskId: this.taskId,
|
||||
taskType: this.taskType,
|
||||
data: this.data,
|
||||
message: message
|
||||
message
|
||||
});
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ class TaskContext {
|
||||
taskId: this.taskId,
|
||||
taskType: this.taskType,
|
||||
data: this.data,
|
||||
result: result
|
||||
result
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
export interface TaskData {
|
||||
safeImport?: boolean;
|
||||
textImportedAsText?: boolean;
|
||||
codeImportedAsCode?: boolean;
|
||||
shrinkImages?: boolean;
|
||||
replaceUnderscoresWithSpaces?: boolean;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { WebSocketServer as WebSocketServer, WebSocket } from "ws";
|
||||
import { isDev, isElectron, randomString } from "./utils.js";
|
||||
import { isElectron, randomString } from "./utils.js";
|
||||
import log from "./log.js";
|
||||
import sql from "./sql.js";
|
||||
import cls from "./cls.js";
|
||||
@ -15,52 +15,6 @@ import { WebSocketMessage, type EntityChange } from "@triliumnext/commons";
|
||||
let webSocketServer!: WebSocketServer;
|
||||
let lastSyncedPush: number | null = null;
|
||||
|
||||
interface Message {
|
||||
type: string;
|
||||
data?: {
|
||||
lastSyncedPush?: number | null;
|
||||
entityChanges?: any[];
|
||||
shrinkImages?: boolean;
|
||||
} | null;
|
||||
lastSyncedPush?: number | null;
|
||||
|
||||
progressCount?: number;
|
||||
taskId?: string;
|
||||
taskType?: string | null;
|
||||
message?: string;
|
||||
reason?: string;
|
||||
result?: string | Record<string, string | undefined>;
|
||||
|
||||
script?: string;
|
||||
params?: any[];
|
||||
noteId?: string;
|
||||
messages?: string[];
|
||||
startNoteId?: string;
|
||||
currentNoteId?: string;
|
||||
entityType?: string;
|
||||
entityId?: string;
|
||||
originEntityName?: "notes";
|
||||
originEntityId?: string | null;
|
||||
lastModifiedMs?: number;
|
||||
filePath?: string;
|
||||
|
||||
// LLM streaming specific fields
|
||||
chatNoteId?: string;
|
||||
content?: string;
|
||||
thinking?: string;
|
||||
toolExecution?: {
|
||||
action?: string;
|
||||
tool?: string;
|
||||
toolCallId?: string;
|
||||
result?: string | Record<string, any>;
|
||||
error?: string;
|
||||
args?: Record<string, unknown>;
|
||||
};
|
||||
done?: boolean;
|
||||
error?: string;
|
||||
raw?: unknown;
|
||||
}
|
||||
|
||||
type SessionParser = (req: IncomingMessage, params: {}, cb: () => void) => void;
|
||||
function init(httpServer: HttpServer, sessionParser: SessionParser) {
|
||||
webSocketServer = new WebSocketServer({
|
||||
@ -106,7 +60,7 @@ Stack: ${message.stack}`);
|
||||
});
|
||||
}
|
||||
|
||||
function sendMessage(client: WebSocket, message: Message) {
|
||||
function sendMessage(client: WebSocket, message: WebSocketMessage) {
|
||||
const jsonStr = JSON.stringify(message);
|
||||
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
@ -114,7 +68,7 @@ function sendMessage(client: WebSocket, message: Message) {
|
||||
}
|
||||
}
|
||||
|
||||
function sendMessageToAllClients(message: Message) {
|
||||
function sendMessageToAllClients(message: WebSocketMessage) {
|
||||
const jsonStr = JSON.stringify(message);
|
||||
|
||||
if (webSocketServer) {
|
||||
|
@ -270,3 +270,96 @@ export interface EntityChangeRecord {
|
||||
entityChange: EntityChange;
|
||||
entity?: EntityRow;
|
||||
}
|
||||
|
||||
type TaskStatus<TypeT, DataT> = {
|
||||
type: "taskProgressCount",
|
||||
taskId: string;
|
||||
taskType: TypeT;
|
||||
data: DataT,
|
||||
progressCount: number
|
||||
} | {
|
||||
type: "taskError",
|
||||
taskId: string;
|
||||
taskType: TypeT;
|
||||
data: DataT;
|
||||
message: string;
|
||||
} | {
|
||||
type: "taskSucceeded",
|
||||
taskId: string;
|
||||
taskType: TypeT;
|
||||
data: DataT;
|
||||
result?: string | Record<string, string | undefined>
|
||||
}
|
||||
|
||||
type TaskDefinitions =
|
||||
TaskStatus<"protectNotes", { protect: boolean; }>
|
||||
| TaskStatus<"importNotes", null>
|
||||
| TaskStatus<"importAttachments", null>
|
||||
| TaskStatus<"deleteNotes", null>
|
||||
| TaskStatus<"undeleteNotes", null>
|
||||
| TaskStatus<"export", null>
|
||||
;
|
||||
|
||||
export interface OpenedFileUpdateStatus {
|
||||
entityType: string;
|
||||
entityId: string;
|
||||
lastModifiedMs?: number;
|
||||
filePath: string;
|
||||
}
|
||||
|
||||
export type WebSocketMessage = TaskDefinitions | {
|
||||
type: "ping"
|
||||
} | {
|
||||
type: "frontend-update",
|
||||
data: {
|
||||
lastSyncedPush: number,
|
||||
entityChanges: EntityChange[]
|
||||
}
|
||||
} | {
|
||||
type: "openNote",
|
||||
noteId: string
|
||||
} | OpenedFileUpdateStatus & {
|
||||
type: "openedFileUpdated"
|
||||
} | {
|
||||
type: "protectedSessionLogin"
|
||||
} | {
|
||||
type: "protectedSessionLogout"
|
||||
} | {
|
||||
type: "toast",
|
||||
message: string;
|
||||
} | {
|
||||
type: "api-log-messages",
|
||||
noteId: string,
|
||||
messages: string[]
|
||||
} | {
|
||||
type: "execute-script";
|
||||
script: string;
|
||||
params: unknown[];
|
||||
startNoteId?: string;
|
||||
currentNoteId: string;
|
||||
originEntityName: string;
|
||||
originEntityId?: string | null;
|
||||
} | {
|
||||
type: "reload-frontend";
|
||||
reason: string;
|
||||
} | {
|
||||
type: "sync-pull-in-progress" | "sync-push-in-progress" | "sync-finished" | "sync-failed";
|
||||
lastSyncedPush: number;
|
||||
} | {
|
||||
type: "consistency-checks-failed"
|
||||
} | {
|
||||
type: "llm-stream",
|
||||
chatNoteId: string;
|
||||
done?: boolean;
|
||||
error?: string;
|
||||
thinking?: string;
|
||||
content?: string;
|
||||
toolExecution?: {
|
||||
action?: string;
|
||||
tool?: string;
|
||||
toolCallId?: string;
|
||||
result?: string | Record<string, any>;
|
||||
error?: string;
|
||||
args?: Record<string, unknown>;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user