Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ export function createOutputApi(chatCompleteApi: ChatCompleteAPI) {
return {
id,
output:
event.toolCalls.length && 'arguments' in event.toolCalls[0].function
? event.toolCalls[0].function.arguments
event?.toolCalls?.length && 'arguments' in event?.toolCalls[0]?.function
? event.toolCalls[0]?.function?.arguments
: undefined,
content: event.content,
type: OutputEventType.OutputComplete,
Expand All @@ -107,8 +107,8 @@ export function createOutputApi(chatCompleteApi: ChatCompleteAPI) {
id,
content: chatResponse.content,
output:
chatResponse.toolCalls.length && 'arguments' in chatResponse.toolCalls[0].function
? chatResponse.toolCalls[0].function.arguments
chatResponse?.toolCalls?.length && 'arguments' in chatResponse?.toolCalls[0]?.function
? chatResponse?.toolCalls[0]?.function?.arguments
: undefined,
};
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,20 @@ The generated documentation is validated and will emit warnings when invalid que
- checked out `built-docs` repo in the same folder as the `kibana` repository
- a running Kibana instance
- an installed Generative AI connector

### Run script to generate ES|QL docs and verify syntax

```
node x-pack/platform/plugins/shared/inference/scripts/load_esql_docs/index.js
```

The script will also generate a report of syntax errors found during the generation process, located at
`x-pack/platform/plugins/shared/inference/server/tasks/nl_to_esql/esql_docs/__tmp__/syntax-errors.json`. This file will not be checked into git.

### Checking syntax errors for generated files

After making modifications to fix the syntax error, and you just need to check if there any remaining errors left,you can run a script that reports.

```
node x-pack/platform/plugins/shared/inference/scripts/report_syntax_errors/index.js
```
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ export async function extractDocEntries({
log: ToolingLog;
inferenceClient: ScriptInferenceClient;
}): Promise<ExtractionOutput> {
const files = await fastGlob(`${builtDocsDir}/html/en/elasticsearch/reference/master/esql*.html`);
const path = `${builtDocsDir}/html/en/elasticsearch/reference/current/esql*.html`;
const files = await fastGlob(path);
if (!files.length) {
throw new Error('No files found');
throw new Error(`No files found at path: ${path}`);
}

const output: ExtractionOutput = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
*/

import { run } from '@kbn/dev-cli-runner';
import { ESQLMessage, EditorError } from '@kbn/esql-ast';
import { validateQuery } from '@kbn/esql-validation-autocomplete';
import Fs from 'fs/promises';
import Path from 'path';
import yargs, { Argv } from 'yargs';
Expand All @@ -20,7 +18,8 @@ import { KibanaClient } from '../util/kibana_client';
import { selectConnector } from '../util/select_connector';
import { syncBuiltDocs } from './sync_built_docs_repo';
import { extractDocEntries } from './extract_doc_entries';
import { generateDoc, FileToWrite } from './generate_doc';
import { generateDoc } from './generate_doc';
import { reportSyntaxErrors } from '../report_syntax_errors/report_syntax_errors';

yargs(process.argv.slice(2))
.command(
Expand Down Expand Up @@ -128,49 +127,10 @@ yargs(process.argv.slice(2))
);
}

log.info(`Checking syntax...`);
const syntaxErrors = (
await Promise.all(docFiles.map(async (file) => await findEsqlSyntaxError(file)))
).flat();

log.warning(
`Please verify the following queries that had syntax errors\n${JSON.stringify(
syntaxErrors,
null,
2
)}`
);
await reportSyntaxErrors(outDir, log, docFiles);
},
{ log: { defaultLevel: argv.logLevel as any }, flags: { allowUnexpected: true } }
);
}
)
.parse();

interface SyntaxError {
query: string;
errors: Array<ESQLMessage | EditorError>;
}

const findEsqlSyntaxError = async (doc: FileToWrite): Promise<SyntaxError[]> => {
return Array.from(doc.content.matchAll(INLINE_ESQL_QUERY_REGEX)).reduce(
async (listP, [match, query]) => {
const list = await listP;
const { errors, warnings } = await validateQuery(query, {
// setting this to true, we don't want to validate the index / fields existence
ignoreOnMissingCallbacks: true,
});

const all = [...errors, ...warnings];
if (all.length) {
list.push({
errors: all,
query,
});
}

return list;
},
Promise.resolve([] as SyntaxError[])
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* 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.
*/

require('@kbn/babel-register').install();

require('./report_syntax_errors');
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* 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 { ESQLMessage, EditorError } from '@kbn/esql-ast';
import { validateQuery } from '@kbn/esql-validation-autocomplete';
import Fs from 'fs/promises';
import Path from 'path';
import yargs, { Argv } from 'yargs';
import type { ToolingLog } from '@kbn/tooling-log';
import { run } from '@kbn/dev-cli-runner';
import { INLINE_ESQL_QUERY_REGEX } from '../../common/tasks/nl_to_esql/constants';
import type { FileToWrite } from '../load_esql_docs/generate_doc';

interface SyntaxError {
query: string;
errors: Array<ESQLMessage | EditorError>;
}

/**
* Log out syntax errors and also write them to a {outDir}/__tmp__/syntax-errors.json
* If docsToCheck is provided, they will be used instead of reading the files from the outDir.
* @param docFiles - The files to check for syntax errors.
* @param outDir - The directory to write the syntax errors to.
* @param log - The logger to use.
*/
export const reportSyntaxErrors = async (
outDir: string,
log: ToolingLog,
docsToCheck?: FileToWrite[]
) => {
let docFiles: FileToWrite[] | undefined = docsToCheck;
if (docsToCheck) {
log.info(`Checking syntax for ${docsToCheck.length} provided files`);
} else {
log.info(`Checking syntax for files in ${outDir}`);
docFiles = await Fs.readdir(outDir).then(async (files) => {
return await Promise.all(
files
.filter((file) => file.endsWith('.txt'))
.map(async (file) => {
const content = await Fs.readFile(Path.join(outDir, file), 'utf8');
return {
name: file,
content,
};
})
);
});
log.info(`Found ${(docFiles ?? []).length} files to check in ${outDir}`);
}

if (!docFiles) return;

const syntaxErrors = (
await Promise.all(docFiles.map(async (file) => await findEsqlSyntaxError(file)))
).flat();

log.warning(
`Please verify the following queries that had syntax errors\n${JSON.stringify(
syntaxErrors,
null,
2
)}`
);
if (syntaxErrors.length > 0) {
const tmpDir = Path.join(outDir, '__tmp__');
await Fs.mkdir(tmpDir).catch((error) => (error.code === 'EEXIST' ? Promise.resolve() : error));
const syntaxErrorsFile = Path.join(tmpDir, 'syntax-errors.json');
await Fs.writeFile(syntaxErrorsFile, JSON.stringify(syntaxErrors, null, 2));
log.info(`Syntax errors written to ${syntaxErrorsFile}`);
}
};
const findEsqlSyntaxError = async (doc: FileToWrite): Promise<SyntaxError[]> => {
return Array.from(doc.content.matchAll(INLINE_ESQL_QUERY_REGEX)).reduce(
async (listP, [match, query]) => {
const list = await listP;
const { errors, warnings } = await validateQuery(query, {
// setting this to true, we don't want to validate the index / fields existence
ignoreOnMissingCallbacks: true,
});

const all = [...errors, ...warnings];
if (all.length) {
list.push({
errors: all,
query,
});
}

return list;
},
Promise.resolve([] as SyntaxError[])
);
};

yargs(process.argv.slice(2))
.command(
'*',
'Extract ES|QL documentation',
(y: Argv) =>
y
.option('logLevel', {
describe: 'Log level',
string: true,
default: process.env.LOG_LEVEL || 'info',
choices: ['info', 'debug', 'silent', 'verbose'],
})
.option('outDir', {
describe: 'The directory to write the syntax errors to.',
default: Path.join(__dirname, '../../server/tasks/nl_to_esql/esql_docs'),
})
.parse(),
(argv) => {
run(
async ({ log }) => {
const outDir = argv.outDir as string;
await reportSyntaxErrors(outDir, log);
},
{ log: { defaultLevel: argv.logLevel as any }, flags: { allowUnexpected: true } }
);
}
)
.parse();
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
# ABS

The ABS function returns the absolute value of a given number.
The `ABS` function returns the absolute value of a numeric expression.

## Syntax

`ABS(number)`

### Parameters

#### number
#### `number`

A numeric expression. If the parameter is `null`, the function will also return `null`.
A numeric expression. If the value is `null`, the function returns `null`.

## Examples

In this example, the ABS function is used to calculate the absolute value of -1.0:

```esql
ROW number = -1.0
| EVAL abs_number = ABS(number)
```

In the following example, the ABS function is used to calculate the absolute value of the height of employees:
Calculate the absolute value of a negative number.

```esql
FROM employees
| KEEP first_name, last_name, height
| EVAL abs_height = ABS(0.0 - height)
```
```

Calculate the absolute value of the difference between `0.0` and the `height` column.
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
# ACOS

The ACOS function returns the arccosine of a given number, expressed in radians.
Returns the arccosine of a number as an angle, expressed in radians.

## Syntax

`ACOS(number)`

### Parameters

#### number
#### `number`

This is a number between -1 and 1. If the parameter is `null`, the function will also return `null`.
- A number between -1 and 1.
- If `null`, the function returns `null`.

## Examples

In this example, the ACOS function calculates the arccosine of 0.9.

```esql
ROW a=.9
| EVAL acos=ACOS(a)
ROW a = .9
| EVAL acos = ACOS(a)
```

```esql
ROW b = -0.5
| EVAL acos_b = ACOS(b)
```
Calculate the arccosine of the value `0.9` and store the result in a new column named `acos`.
Original file line number Diff line number Diff line change
@@ -1,29 +1,23 @@
# ASIN

The ASIN function returns the arcsine of a given numeric expression as an angle, expressed in radians.
Returns the arcsine of the input numeric expression as an angle, expressed in radians.

## Syntax

`ASIN(number)`

### Parameters

#### number
#### `number`

This is a numeric value ranging between -1 and 1. If the parameter is `null`, the function will also return `null`.
- A number between -1 and 1.
- If `null`, the function returns `null`.

## Examples

In this example, the ASIN function calculates the arcsine of 0.9:

```esql
ROW a=.9
| EVAL asin=ASIN(a)
```

In this example, the ASIN function calculates the arcsine of -0.5:

```esql
ROW a = -.5
ROW a = .9
| EVAL asin = ASIN(a)
```

Calculate the arcsine of the value `0.9` and return the result in radians.
Loading