From a89a3f414648a68e6babced77a995f5f4e9bbbaf Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Tue, 29 Apr 2025 09:55:43 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=8B=20fix:=20Improve=20Deepseek=20Comp?= =?UTF-8?q?atbility=20(#7132)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: Update schema conversion to allow nullable optional fields * feat: Add support for 'Deepseek' model in response sender logic * fix: Normalize endpoint case for legacy content handling in AgentClient (fixes `deepseek-chat` followup issues) --- api/server/controllers/agents/client.js | 2 +- packages/data-provider/src/parsers.ts | 4 +++ packages/data-provider/src/zod.ts | 45 ++++++++++++------------- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/api/server/controllers/agents/client.js b/api/server/controllers/agents/client.js index cb4a9347cb..1097ddd390 100644 --- a/api/server/controllers/agents/client.js +++ b/api/server/controllers/agents/client.js @@ -673,7 +673,7 @@ class AgentClient extends BaseClient { this.indexTokenCountMap, toolSet, ); - if (legacyContentEndpoints.has(this.options.agent.endpoint)) { + if (legacyContentEndpoints.has(this.options.agent.endpoint?.toLowerCase())) { initialMessages = formatContentStrings(initialMessages); } diff --git a/packages/data-provider/src/parsers.ts b/packages/data-provider/src/parsers.ts index dae0723457..2389647eef 100644 --- a/packages/data-provider/src/parsers.ts +++ b/packages/data-provider/src/parsers.ts @@ -253,6 +253,8 @@ export const getResponseSender = (endpointOption: t.TEndpointOption): string => return extractOmniVersion(model); } else if (model && (model.includes('mistral') || model.includes('codestral'))) { return 'Mistral'; + } else if (model && model.includes('deepseek')) { + return 'Deepseek'; } else if (model && model.includes('gpt-')) { const gptVersion = extractGPTVersion(model); return gptVersion || 'GPT'; @@ -289,6 +291,8 @@ export const getResponseSender = (endpointOption: t.TEndpointOption): string => return extractOmniVersion(model); } else if (model && (model.includes('mistral') || model.includes('codestral'))) { return 'Mistral'; + } else if (model && model.includes('deepseek')) { + return 'Deepseek'; } else if (model && model.includes('gpt-')) { const gptVersion = extractGPTVersion(model); return gptVersion || 'GPT'; diff --git a/packages/data-provider/src/zod.ts b/packages/data-provider/src/zod.ts index 8e5daf1102..bffc693b4a 100644 --- a/packages/data-provider/src/zod.ts +++ b/packages/data-provider/src/zod.ts @@ -29,7 +29,9 @@ function dropSchemaFields( schema: JsonSchemaType | undefined, fields: string[], ): JsonSchemaType | undefined { - if (schema == null || typeof schema !== 'object') {return schema;} + if (schema == null || typeof schema !== 'object') { + return schema; + } // Handle arrays (should only occur for enum, required, etc.) if (Array.isArray(schema)) { // This should not happen for the root schema, but for completeness: @@ -37,33 +39,25 @@ function dropSchemaFields( } const result: Record = {}; for (const [key, value] of Object.entries(schema)) { - if (fields.includes(key)) {continue;} + if (fields.includes(key)) { + continue; + } // Recursively process nested schemas - if ( - key === 'items' || - key === 'additionalProperties' || - key === 'properties' - ) { + if (key === 'items' || key === 'additionalProperties' || key === 'properties') { if (key === 'properties' && value && typeof value === 'object') { // properties is a record of string -> JsonSchemaType const newProps: Record = {}; for (const [propKey, propValue] of Object.entries( value as Record, )) { - const dropped = dropSchemaFields( - propValue, - fields, - ); + const dropped = dropSchemaFields(propValue, fields); if (dropped !== undefined) { newProps[propKey] = dropped; } } result[key] = newProps; } else if (key === 'items' || key === 'additionalProperties') { - const dropped = dropSchemaFields( - value as JsonSchemaType, - fields, - ); + const dropped = dropSchemaFields(value as JsonSchemaType, fields); if (dropped !== undefined) { result[key] = dropped; } @@ -127,12 +121,12 @@ function convertToZodUnion( // Special handling for schemas that add properties if (subSchema.properties && Object.keys(subSchema.properties).length > 0) { - // Create a schema with the properties and make them all optional + // Create a schema with the properties and make them all optional const objSchema = { type: 'object', properties: subSchema.properties, additionalProperties: true, // Allow additional properties - // Don't include required here to make all properties optional + // Don't include required here to make all properties optional } as JsonSchemaType; // Convert to Zod schema @@ -141,15 +135,17 @@ function convertToZodUnion( // For the special case of { optional: true } if ('optional' in (subSchema.properties as Record)) { // Create a custom schema that preserves the optional property - const customSchema = z.object({ - optional: z.boolean(), - }).passthrough(); + const customSchema = z + .object({ + optional: z.boolean(), + }) + .passthrough(); return customSchema; } if (zodSchema instanceof z.ZodObject) { - // Make sure the schema allows additional properties + // Make sure the schema allows additional properties return zodSchema.passthrough(); } return zodSchema; @@ -362,12 +358,15 @@ export function convertJsonSchemaToZod( const partial = Object.fromEntries( Object.entries(shape).map(([key, value]) => [ key, - schema.required?.includes(key) === true ? value : value.optional(), + schema.required?.includes(key) === true ? value : value.optional().nullable(), ]), ); objectSchema = z.object(partial); } else { - objectSchema = objectSchema.partial(); + const partialNullable = Object.fromEntries( + Object.entries(shape).map(([key, value]) => [key, value.optional().nullable()]), + ); + objectSchema = z.object(partialNullable); } // Handle additionalProperties for open-ended objects