🐋 fix: Improve Deepseek Compatbility (#7132)

* 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)
This commit is contained in:
Danny Avila 2025-04-29 09:55:43 -04:00 committed by GitHub
parent 55f5f2d11a
commit a89a3f4146
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 27 additions and 24 deletions

View file

@ -673,7 +673,7 @@ class AgentClient extends BaseClient {
this.indexTokenCountMap, this.indexTokenCountMap,
toolSet, toolSet,
); );
if (legacyContentEndpoints.has(this.options.agent.endpoint)) { if (legacyContentEndpoints.has(this.options.agent.endpoint?.toLowerCase())) {
initialMessages = formatContentStrings(initialMessages); initialMessages = formatContentStrings(initialMessages);
} }

View file

@ -253,6 +253,8 @@ export const getResponseSender = (endpointOption: t.TEndpointOption): string =>
return extractOmniVersion(model); return extractOmniVersion(model);
} else if (model && (model.includes('mistral') || model.includes('codestral'))) { } else if (model && (model.includes('mistral') || model.includes('codestral'))) {
return 'Mistral'; return 'Mistral';
} else if (model && model.includes('deepseek')) {
return 'Deepseek';
} else if (model && model.includes('gpt-')) { } else if (model && model.includes('gpt-')) {
const gptVersion = extractGPTVersion(model); const gptVersion = extractGPTVersion(model);
return gptVersion || 'GPT'; return gptVersion || 'GPT';
@ -289,6 +291,8 @@ export const getResponseSender = (endpointOption: t.TEndpointOption): string =>
return extractOmniVersion(model); return extractOmniVersion(model);
} else if (model && (model.includes('mistral') || model.includes('codestral'))) { } else if (model && (model.includes('mistral') || model.includes('codestral'))) {
return 'Mistral'; return 'Mistral';
} else if (model && model.includes('deepseek')) {
return 'Deepseek';
} else if (model && model.includes('gpt-')) { } else if (model && model.includes('gpt-')) {
const gptVersion = extractGPTVersion(model); const gptVersion = extractGPTVersion(model);
return gptVersion || 'GPT'; return gptVersion || 'GPT';

View file

@ -29,7 +29,9 @@ function dropSchemaFields(
schema: JsonSchemaType | undefined, schema: JsonSchemaType | undefined,
fields: string[], fields: string[],
): JsonSchemaType | undefined { ): 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.) // Handle arrays (should only occur for enum, required, etc.)
if (Array.isArray(schema)) { if (Array.isArray(schema)) {
// This should not happen for the root schema, but for completeness: // This should not happen for the root schema, but for completeness:
@ -37,33 +39,25 @@ function dropSchemaFields(
} }
const result: Record<string, unknown> = {}; const result: Record<string, unknown> = {};
for (const [key, value] of Object.entries(schema)) { for (const [key, value] of Object.entries(schema)) {
if (fields.includes(key)) {continue;} if (fields.includes(key)) {
continue;
}
// Recursively process nested schemas // Recursively process nested schemas
if ( if (key === 'items' || key === 'additionalProperties' || key === 'properties') {
key === 'items' ||
key === 'additionalProperties' ||
key === 'properties'
) {
if (key === 'properties' && value && typeof value === 'object') { if (key === 'properties' && value && typeof value === 'object') {
// properties is a record of string -> JsonSchemaType // properties is a record of string -> JsonSchemaType
const newProps: Record<string, JsonSchemaType> = {}; const newProps: Record<string, JsonSchemaType> = {};
for (const [propKey, propValue] of Object.entries( for (const [propKey, propValue] of Object.entries(
value as Record<string, JsonSchemaType>, value as Record<string, JsonSchemaType>,
)) { )) {
const dropped = dropSchemaFields( const dropped = dropSchemaFields(propValue, fields);
propValue,
fields,
);
if (dropped !== undefined) { if (dropped !== undefined) {
newProps[propKey] = dropped; newProps[propKey] = dropped;
} }
} }
result[key] = newProps; result[key] = newProps;
} else if (key === 'items' || key === 'additionalProperties') { } else if (key === 'items' || key === 'additionalProperties') {
const dropped = dropSchemaFields( const dropped = dropSchemaFields(value as JsonSchemaType, fields);
value as JsonSchemaType,
fields,
);
if (dropped !== undefined) { if (dropped !== undefined) {
result[key] = dropped; result[key] = dropped;
} }
@ -127,12 +121,12 @@ function convertToZodUnion(
// Special handling for schemas that add properties // Special handling for schemas that add properties
if (subSchema.properties && Object.keys(subSchema.properties).length > 0) { 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 = { const objSchema = {
type: 'object', type: 'object',
properties: subSchema.properties, properties: subSchema.properties,
additionalProperties: true, // Allow additional 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; } as JsonSchemaType;
// Convert to Zod schema // Convert to Zod schema
@ -141,15 +135,17 @@ function convertToZodUnion(
// For the special case of { optional: true } // For the special case of { optional: true }
if ('optional' in (subSchema.properties as Record<string, unknown>)) { if ('optional' in (subSchema.properties as Record<string, unknown>)) {
// Create a custom schema that preserves the optional property // Create a custom schema that preserves the optional property
const customSchema = z.object({ const customSchema = z
optional: z.boolean(), .object({
}).passthrough(); optional: z.boolean(),
})
.passthrough();
return customSchema; return customSchema;
} }
if (zodSchema instanceof z.ZodObject) { if (zodSchema instanceof z.ZodObject) {
// Make sure the schema allows additional properties // Make sure the schema allows additional properties
return zodSchema.passthrough(); return zodSchema.passthrough();
} }
return zodSchema; return zodSchema;
@ -362,12 +358,15 @@ export function convertJsonSchemaToZod(
const partial = Object.fromEntries( const partial = Object.fromEntries(
Object.entries(shape).map(([key, value]) => [ Object.entries(shape).map(([key, value]) => [
key, key,
schema.required?.includes(key) === true ? value : value.optional(), schema.required?.includes(key) === true ? value : value.optional().nullable(),
]), ]),
); );
objectSchema = z.object(partial); objectSchema = z.object(partial);
} else { } 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 // Handle additionalProperties for open-ended objects