forked from elastic/kibana
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Auto Import] Fix cases where LLM generates incorrect array field acc…
…ess (elastic#196207) ## Release Note Fixes cases where LLM was likely to generate invalid processors containing array access in Automatic Import. ## Context Previously, it happened from time to time that the LLM attempts to add related fields or apply categorization conditions that use a field, path to which goes through an array. The problem is that such an access is invalid and leads to an immediate error (key part highlighted): Even including explicit instructions to avoid brackets or an array access did not seem enough, as the LLM would try to use a different syntax, owing to the aggressiveness of our review instructions. The suggested solution is to remove all arrays from the information shown to the LLM in the related chain. This guarantees that no illegal access will ever be attempted. ### Summary - Introduces a utility function to remove all arrays from a JSON object. - Applies this function for all LLM calls in the related chain. - Modifies the prompts of related and categorization chain to skip the arrays as well. --------- Co-authored-by: Bharat Pasupula <[email protected]> (cherry picked from commit 8abe259)
- Loading branch information
Showing
6 changed files
with
183 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
135 changes: 135 additions & 0 deletions
135
x-pack/plugins/integration_assistant/server/graphs/related/util.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import { deepCopySkipArrays } from './util'; | ||
|
||
describe('deepCopySkipArrays', () => { | ||
it('should skip arrays and deeply copy objects', () => { | ||
const input = { | ||
field: ['a', 'b'], | ||
another: { field: 'c' }, | ||
}; | ||
|
||
const expectedOutput = { | ||
another: { field: 'c' }, | ||
}; | ||
|
||
expect(deepCopySkipArrays(input)).toEqual(expectedOutput); | ||
}); | ||
|
||
it('should return primitive types as is', () => { | ||
expect(deepCopySkipArrays(42)).toBe(42); | ||
expect(deepCopySkipArrays('string')).toBe('string'); | ||
expect(deepCopySkipArrays(true)).toBe(true); | ||
}); | ||
|
||
it('should handle nested objects and skip nested arrays', () => { | ||
const input = { | ||
level1: { | ||
level2: { | ||
array: [1, 2, 3], | ||
value: 'test', | ||
}, | ||
}, | ||
}; | ||
|
||
const expectedOutput = { | ||
level1: { | ||
level2: { | ||
value: 'test', | ||
}, | ||
}, | ||
}; | ||
|
||
expect(deepCopySkipArrays(input)).toEqual(expectedOutput); | ||
}); | ||
|
||
it('should return undefined for arrays', () => { | ||
expect(deepCopySkipArrays([1, 2, 3])).toBeUndefined(); | ||
}); | ||
|
||
it('should handle null and undefined values', () => { | ||
expect(deepCopySkipArrays(null)).toBeNull(); | ||
expect(deepCopySkipArrays(undefined)).toBeUndefined(); | ||
}); | ||
|
||
it('should handle empty objects', () => { | ||
expect(deepCopySkipArrays({})).toEqual({}); | ||
}); | ||
|
||
it('should handle objects with mixed types', () => { | ||
const input = { | ||
number: 1, | ||
string: 'test', | ||
boolean: true, | ||
object: { key: 'value' }, | ||
array: [1, 2, 3], | ||
}; | ||
|
||
const expectedOutput = { | ||
number: 1, | ||
string: 'test', | ||
boolean: true, | ||
object: { key: 'value' }, | ||
}; | ||
|
||
expect(deepCopySkipArrays(input)).toEqual(expectedOutput); | ||
}); | ||
|
||
// Test case | ||
it('should skip arrays and deeply copy objects with nested arrays', () => { | ||
const input = { | ||
field: ['a', 'b'], | ||
another: { field: 'c' }, | ||
}; | ||
|
||
const expectedOutput = { | ||
another: { field: 'c' }, | ||
}; | ||
|
||
expect(deepCopySkipArrays(input)).toEqual(expectedOutput); | ||
}); | ||
|
||
it('should handle objects with nested empty arrays', () => { | ||
const input = { | ||
field: [], | ||
another: { field: 'c' }, | ||
}; | ||
|
||
const expectedOutput = { | ||
another: { field: 'c' }, | ||
}; | ||
|
||
expect(deepCopySkipArrays(input)).toEqual(expectedOutput); | ||
}); | ||
|
||
it('should handle objects with nested arrays containing objects', () => { | ||
const input = { | ||
field: [{ key: 'value' }], | ||
another: { field: 'c' }, | ||
}; | ||
|
||
const expectedOutput = { | ||
another: { field: 'c' }, | ||
}; | ||
|
||
expect(deepCopySkipArrays(input)).toEqual(expectedOutput); | ||
}); | ||
|
||
it('should handle objects with nested arrays containing mixed types', () => { | ||
const input = { | ||
field: [1, 'string', true, { key: 'value' }], | ||
another: { field: 'c' }, | ||
}; | ||
|
||
const expectedOutput = { | ||
another: { field: 'c' }, | ||
}; | ||
|
||
expect(deepCopySkipArrays(input)).toEqual(expectedOutput); | ||
}); | ||
}); |
37 changes: 37 additions & 0 deletions
37
x-pack/plugins/integration_assistant/server/graphs/related/util.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
/** | ||
* Deeply copies a JSON object, skipping all arrays. | ||
* | ||
* @param value - The JSON value to be deeply copied, which can be an array, object, or other types. | ||
* @returns A new object that is a deep copy of the input value, but with arrays skipped. | ||
* | ||
* This function recursively traverses the provided value. If the value is an array, it skips it. | ||
* If the value is a regular object, it continues traversing its properties and copying them. | ||
*/ | ||
export function deepCopySkipArrays(value: unknown): unknown { | ||
if (Array.isArray(value)) { | ||
// Skip arrays | ||
return undefined; | ||
} | ||
|
||
if (typeof value === 'object' && value !== null) { | ||
// Regular dictionary, continue traversing. | ||
const result: Record<string, unknown> = {}; | ||
for (const [k, v] of Object.entries(value)) { | ||
const copiedValue = deepCopySkipArrays(v); | ||
if (copiedValue !== undefined) { | ||
result[k] = copiedValue; | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
// For primitive types, return the value as is. | ||
return value; | ||
} |