mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-23 08:00:45 +00:00
better error handling + cleaning
This commit is contained in:
@@ -437,6 +437,7 @@ class AIChatManager {
|
||||
}
|
||||
return addedMessages
|
||||
} catch (err) {
|
||||
console.error('Error in chatRequest', err)
|
||||
callbacks.onMessageEnd()
|
||||
if (!abortController.signal.aborted) {
|
||||
throw err
|
||||
|
||||
@@ -59,6 +59,7 @@ export async function parseAnthropicCompletion(
|
||||
helpers: any
|
||||
): Promise<boolean> {
|
||||
let toolCallsToProcess: ChatCompletionMessageFunctionToolCall[] = []
|
||||
let error = null
|
||||
|
||||
// Handle text streaming
|
||||
completion.on('text', (textDelta: string, _textSnapshot: string) => {
|
||||
@@ -95,7 +96,7 @@ export async function parseAnthropicCompletion(
|
||||
// Handle errors
|
||||
completion.on('error', (error: any) => {
|
||||
console.error('Anthropic stream error:', error)
|
||||
throw error
|
||||
error = error
|
||||
})
|
||||
|
||||
// Wait for completion
|
||||
@@ -103,6 +104,10 @@ export async function parseAnthropicCompletion(
|
||||
|
||||
callbacks.onMessageEnd()
|
||||
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
// Process tool calls if any
|
||||
if (toolCallsToProcess.length > 0) {
|
||||
const assistantWithTools = {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { ChatCompletionTool } from 'openai/resources/index.mjs'
|
||||
import type { ChatCompletionFunctionTool } from 'openai/resources/index.mjs'
|
||||
import type { Tool } from '../shared'
|
||||
import { get } from 'svelte/store'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import type { EndpointTool } from '$lib/gen/types.gen'
|
||||
import { McpService } from '$lib/gen/services.gen'
|
||||
|
||||
function buildApiCallTool(endpointTool: EndpointTool): ChatCompletionTool {
|
||||
function buildApiCallTool(endpointTool: EndpointTool): ChatCompletionFunctionTool {
|
||||
// Build the parameters schema for OpenAI function calling
|
||||
const parameters: Record<string, any> = {
|
||||
type: 'object',
|
||||
@@ -18,10 +18,13 @@ function buildApiCallTool(endpointTool: EndpointTool): ChatCompletionTool {
|
||||
for (const [key, schema] of Object.entries(endpointTool.path_params_schema.properties)) {
|
||||
// Skip workspace parameter as it's auto-filled
|
||||
if (key === 'workspace') continue
|
||||
|
||||
|
||||
parameters.properties[key] = schema
|
||||
|
||||
if (Array.isArray(endpointTool.path_params_schema.required) && endpointTool.path_params_schema.required.includes(key)) {
|
||||
|
||||
if (
|
||||
Array.isArray(endpointTool.path_params_schema.required) &&
|
||||
endpointTool.path_params_schema.required.includes(key)
|
||||
) {
|
||||
parameters.required.push(key)
|
||||
}
|
||||
}
|
||||
@@ -31,8 +34,11 @@ function buildApiCallTool(endpointTool: EndpointTool): ChatCompletionTool {
|
||||
if (endpointTool.query_params_schema?.properties) {
|
||||
for (const [key, schema] of Object.entries(endpointTool.query_params_schema.properties)) {
|
||||
parameters.properties[key] = schema
|
||||
|
||||
if (Array.isArray(endpointTool.query_params_schema.required) && endpointTool.query_params_schema.required.includes(key)) {
|
||||
|
||||
if (
|
||||
Array.isArray(endpointTool.query_params_schema.required) &&
|
||||
endpointTool.query_params_schema.required.includes(key)
|
||||
) {
|
||||
parameters.required.push(key)
|
||||
}
|
||||
}
|
||||
@@ -47,8 +53,11 @@ function buildApiCallTool(endpointTool: EndpointTool): ChatCompletionTool {
|
||||
properties: endpointTool.body_schema.properties,
|
||||
required: endpointTool.body_schema.required || []
|
||||
}
|
||||
|
||||
if (Array.isArray(endpointTool.body_schema.required) && endpointTool.body_schema.required.length > 0) {
|
||||
|
||||
if (
|
||||
Array.isArray(endpointTool.body_schema.required) &&
|
||||
endpointTool.body_schema.required.length > 0
|
||||
) {
|
||||
parameters.required.push('body')
|
||||
}
|
||||
}
|
||||
@@ -63,16 +72,17 @@ function buildApiCallTool(endpointTool: EndpointTool): ChatCompletionTool {
|
||||
}
|
||||
}
|
||||
|
||||
function buildToolsFromEndpoints(
|
||||
endpointTools: EndpointTool[]
|
||||
): { tools: ChatCompletionTool[]; endpointMap: Record<string, { method: string; path: string }> } {
|
||||
const tools: ChatCompletionTool[] = []
|
||||
function buildToolsFromEndpoints(endpointTools: EndpointTool[]): {
|
||||
tools: ChatCompletionFunctionTool[]
|
||||
endpointMap: Record<string, { method: string; path: string }>
|
||||
} {
|
||||
const tools: ChatCompletionFunctionTool[] = []
|
||||
const endpointMap: Record<string, { method: string; path: string }> = {}
|
||||
|
||||
for (const endpointTool of endpointTools) {
|
||||
const tool = buildApiCallTool(endpointTool)
|
||||
tools.push(tool)
|
||||
|
||||
|
||||
// Store the endpoint info in the map
|
||||
endpointMap[endpointTool.name] = {
|
||||
method: endpointTool.method,
|
||||
@@ -84,17 +94,17 @@ function buildToolsFromEndpoints(
|
||||
}
|
||||
|
||||
export function createApiTools(
|
||||
chatTools: ChatCompletionTool[],
|
||||
chatTools: ChatCompletionFunctionTool[],
|
||||
endpointMap: Record<string, { method: string; path: string }> = {}
|
||||
): Tool<{}>[] {
|
||||
return chatTools.map((chatTool) => {
|
||||
const toolName = chatTool.function.name
|
||||
const endpoint = endpointMap[toolName]
|
||||
const method = endpoint?.method?.toUpperCase() || 'GET'
|
||||
|
||||
|
||||
// Determine if tool needs confirmation based on method
|
||||
const needsConfirmation = ['DELETE', 'POST', 'PUT', 'PATCH'].includes(method)
|
||||
|
||||
|
||||
return {
|
||||
def: chatTool,
|
||||
requiresConfirmation: needsConfirmation,
|
||||
@@ -103,7 +113,7 @@ export function createApiTools(
|
||||
fn: async ({ args, toolId, toolCallbacks }) => {
|
||||
const toolName = chatTool.function.name
|
||||
const endpoint = endpointMap[toolName]
|
||||
|
||||
|
||||
if (!endpoint) {
|
||||
throw new Error(`No endpoint mapping found for tool ${toolName}`)
|
||||
}
|
||||
@@ -111,7 +121,7 @@ export function createApiTools(
|
||||
try {
|
||||
const workspace = get(workspaceStore) as string
|
||||
let path = endpoint.path.replace('{workspace}', workspace)
|
||||
|
||||
|
||||
// Build URL with path parameters
|
||||
let url = `/api${path}`
|
||||
const queryParams: Record<string, string> = {}
|
||||
@@ -143,7 +153,7 @@ export function createApiTools(
|
||||
}
|
||||
|
||||
toolCallbacks.setToolStatus(toolId, {
|
||||
content: `Calling ${toolName}...`,
|
||||
content: `Calling ${toolName}...`
|
||||
})
|
||||
|
||||
const fetchOptions: RequestInit = {
|
||||
@@ -173,7 +183,7 @@ export function createApiTools(
|
||||
})
|
||||
toolCallbacks.setToolStatus(toolId, {
|
||||
content: `Call to ${toolName} completed`,
|
||||
result: jsonResult,
|
||||
result: jsonResult
|
||||
})
|
||||
return jsonResult
|
||||
} else {
|
||||
@@ -186,7 +196,7 @@ export function createApiTools(
|
||||
toolCallbacks.setToolStatus(toolId, {
|
||||
content: `Call to ${toolName} failed`,
|
||||
result: jsonResult,
|
||||
error: `HTTP ${response.status}: ${text}`,
|
||||
error: `HTTP ${response.status}: ${text}`
|
||||
})
|
||||
return jsonResult
|
||||
}
|
||||
@@ -194,7 +204,7 @@ export function createApiTools(
|
||||
const errorMessage = `Error calling API: ${error instanceof Error ? error.message : String(error)}`
|
||||
toolCallbacks.setToolStatus(toolId, {
|
||||
content: `Call to ${toolName} failed`,
|
||||
error: errorMessage,
|
||||
error: errorMessage
|
||||
})
|
||||
console.error(`Error calling API:`, error)
|
||||
return errorMessage
|
||||
@@ -210,10 +220,10 @@ export async function loadApiTools(): Promise<Tool<{}>[]> {
|
||||
const endpointTools = await McpService.listMcpTools({
|
||||
workspace: get(workspaceStore) as string
|
||||
})
|
||||
|
||||
|
||||
// Build tools from the endpoint definitions
|
||||
const { tools: apiTools, endpointMap } = buildToolsFromEndpoints(endpointTools)
|
||||
|
||||
|
||||
// Create executable tools
|
||||
const executableApiTools = createApiTools(apiTools, endpointMap)
|
||||
return executableApiTools
|
||||
@@ -221,4 +231,4 @@ export async function loadApiTools(): Promise<Tool<{}>[]> {
|
||||
console.error('Failed to load API tools:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { get } from 'svelte/store'
|
||||
import { compile, phpCompile, pythonCompile } from '../../utils'
|
||||
import type {
|
||||
ChatCompletionSystemMessageParam,
|
||||
ChatCompletionTool,
|
||||
ChatCompletionFunctionTool,
|
||||
ChatCompletionUserMessageParam
|
||||
} from 'openai/resources/index.mjs'
|
||||
import { type DBSchema, dbSchemas, getCurrentModel } from '$lib/stores'
|
||||
@@ -498,7 +498,7 @@ export function prepareScriptUserMessage(
|
||||
}
|
||||
}
|
||||
|
||||
const RESOURCE_TYPE_FUNCTION_DEF: ChatCompletionTool = {
|
||||
const RESOURCE_TYPE_FUNCTION_DEF: ChatCompletionFunctionTool = {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'search_resource_types',
|
||||
@@ -519,7 +519,7 @@ const RESOURCE_TYPE_FUNCTION_DEF: ChatCompletionTool = {
|
||||
}
|
||||
}
|
||||
|
||||
const DB_SCHEMA_FUNCTION_DEF: ChatCompletionTool = {
|
||||
const DB_SCHEMA_FUNCTION_DEF: ChatCompletionFunctionTool = {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'get_db_schema',
|
||||
@@ -693,7 +693,7 @@ export async function searchExternalIntegrationResources(args: { query: string }
|
||||
}
|
||||
}
|
||||
|
||||
const SEARCH_NPM_PACKAGES_TOOL: ChatCompletionTool = {
|
||||
const SEARCH_NPM_PACKAGES_TOOL: ChatCompletionFunctionTool = {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'search_npm_packages',
|
||||
@@ -778,7 +778,7 @@ export async function fetchNpmPackageTypes(
|
||||
}
|
||||
}
|
||||
|
||||
const TEST_RUN_SCRIPT_TOOL: ChatCompletionTool = {
|
||||
const TEST_RUN_SCRIPT_TOOL: ChatCompletionFunctionTool = {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'test_run_script',
|
||||
|
||||
@@ -669,6 +669,7 @@ export async function parseOpenAICompletion(
|
||||
helpers: any
|
||||
): Promise<boolean> {
|
||||
let toolCallsToProcess: ChatCompletionMessageToolCall[] = []
|
||||
let error = null
|
||||
|
||||
// Handle content streaming
|
||||
stream.on('content.delta', ({ delta }: { delta: string }) => {
|
||||
@@ -677,6 +678,7 @@ export async function parseOpenAICompletion(
|
||||
|
||||
// Handle complete messages
|
||||
stream.on('message', (message: ChatCompletionMessageParam) => {
|
||||
console.log('message', message)
|
||||
if (message.role === 'assistant') {
|
||||
// Add the complete assistant message (whether it has content, tool calls, or both)
|
||||
messages.push(message)
|
||||
@@ -690,6 +692,7 @@ export async function parseOpenAICompletion(
|
||||
toolCallsToProcess = message.tool_calls
|
||||
// Preprocess tools
|
||||
for (const toolCall of message.tool_calls) {
|
||||
console.log('toolCall', toolCall)
|
||||
if (toolCall.type !== 'function') continue
|
||||
const tool = tools.find((t) => t.def.function.name === toolCall.function.name)
|
||||
if (tool?.preAction) {
|
||||
@@ -703,12 +706,16 @@ export async function parseOpenAICompletion(
|
||||
// Handle errors
|
||||
stream.on('error', (error: any) => {
|
||||
console.error('OpenAI stream error:', error)
|
||||
throw error
|
||||
error = error
|
||||
})
|
||||
|
||||
// Wait for completion
|
||||
await stream.done()
|
||||
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
callbacks.onMessageEnd()
|
||||
|
||||
// Process tool calls if any
|
||||
|
||||
Reference in New Issue
Block a user