[Obs AI Assistant] remove AdHocInstruction (#212621)

## Refactor Instruction Handling: Remove Adhoc Instructions and
Standardize API
Closes #211190

## Summary  
This PR removes the concept of *Adhoc Instructions* and standardizes how
instructions are handled across the system. The `/complete` API now
explicitly accepts **user instructions**, and redundant functions have
been removed or replaced.

## Changes Implemented  

### Renamed API Parameter  
- The `/complete` API’s `instructions` parameter is now
`userInstructions`.
- Application instructions can no longer be sent via the API (future
support can be added if needed).

### Removed Redundant Functions  
- Deleted `getAdhocInstructions` and `registerAdhocInstruction`.  
- API-passed instructions are now treated as **user instructions**.  
- Updated function calls to use `getInstructions` and
`registerInstruction` instead.

### Refactored Function Calls  
- Replaced `registerAdhocInstruction` with `registerInstruction`.  

## Impact & Benefits  
- **Simplifies** instruction handling by removing unnecessary
complexity.
- **Aligns** API behavior with internal instruction management.
This commit is contained in:
Arturo Lidueña 2025-03-11 11:24:16 +01:00 committed by GitHub
parent b9472b6ade
commit 56219f1c90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 119 additions and 166 deletions

View file

@ -94,14 +94,6 @@ export interface Instruction {
text: string;
}
export interface AdHocInstruction {
id?: string;
text: string;
instruction_type: 'user_instruction' | 'application_instruction';
}
export type InstructionOrPlainText = string | Instruction;
export enum KnowledgeBaseType {
// user instructions are included in the system prompt regardless of the user's input
UserInstruction = 'user_instruction',

View file

@ -20,7 +20,7 @@ import type {
Message,
ObservabilityAIAssistantScreenContext,
PendingMessage,
AdHocInstruction,
Instruction,
} from '../common/types';
import type { TelemetryEventTypeWithPayload } from './analytics';
import type { ObservabilityAIAssistantAPIClient } from './api';
@ -71,7 +71,7 @@ export interface ObservabilityAIAssistantChatService {
except: string[];
};
signal: AbortSignal;
instructions?: AdHocInstruction[];
instructions?: Array<string | Instruction>;
scopes: AssistantScope[];
}) => Observable<StreamingChatResponseEventWithoutError>;
getFunctions: (options?: {

View file

@ -9,6 +9,7 @@ import { toBooleanRt } from '@kbn/io-ts-utils';
import { context as otelContext } from '@opentelemetry/api';
import * as t from 'io-ts';
import { from, map } from 'rxjs';
import { v4 } from 'uuid';
import { Readable } from 'stream';
import { AssistantScope } from '@kbn/ai-assistant-common';
import { aiAssistantSimulatedFunctionCalling } from '../..';
@ -20,6 +21,7 @@ import { observableIntoStream } from '../../service/util/observable_into_stream'
import { withAssistantSpan } from '../../service/util/with_assistant_span';
import { recallAndScore } from '../../utils/recall/recall_and_score';
import { createObservabilityAIAssistantServerRoute } from '../create_observability_ai_assistant_server_route';
import { Instruction } from '../../../common/types';
import { assistantScopeType, functionRt, messageRt, screenContextRt } from '../runtime_types';
import { ObservabilityAIAssistantRouteHandlerResources } from '../types';
@ -40,14 +42,11 @@ const chatCompleteBaseRt = t.type({
}),
]),
instructions: t.array(
t.intersection([
t.partial({ id: t.string }),
t.union([
t.string,
t.type({
id: t.string,
text: t.string,
instruction_type: t.union([
t.literal('user_instruction'),
t.literal('application_instruction'),
]),
}),
])
),
@ -251,7 +250,7 @@ async function chatComplete(
title,
persist,
screenContexts,
instructions,
instructions: userInstructionsOrStrings,
disableFunctions,
scopes,
},
@ -269,6 +268,16 @@ async function chatComplete(
scopes,
});
const userInstructions: Instruction[] | undefined = userInstructionsOrStrings?.map(
(userInstructionOrString) =>
typeof userInstructionOrString === 'string'
? {
text: userInstructionOrString,
id: v4(),
}
: userInstructionOrString
);
const response$ = client.complete({
messages,
connectorId,
@ -277,7 +286,7 @@ async function chatComplete(
persist,
signal,
functionClient,
instructions,
userInstructions,
simulateFunctionCalling,
disableFunctions,
});

View file

@ -53,7 +53,7 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({
const client = await service.getClient({ request });
const [functionClient, userInstructions] = await Promise.all([
const [functionClient, kbUserInstructions] = await Promise.all([
service.getFunctionClient({
signal: controller.signal,
resources,
@ -73,8 +73,8 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({
functionDefinitions,
systemMessage: getSystemMessageFromInstructions({
applicationInstructions: functionClient.getInstructions(),
userInstructions,
adHocInstructions: functionClient.getAdhocInstructions(),
kbUserInstructions,
apiUserInstructions: [],
availableFunctionNames,
}),
};

View file

@ -7,8 +7,8 @@
import dedent from 'dedent';
import { ChatFunctionClient, GET_DATA_ON_SCREEN_FUNCTION_NAME } from '.';
import { FunctionVisibility } from '../../../common/functions/types';
import { AdHocInstruction } from '../../../common/types';
import { Logger } from '@kbn/logging';
import { RegisterInstructionCallback } from '../types';
describe('chatFunctionClient', () => {
describe('when executing a function with invalid arguments', () => {
@ -89,7 +89,7 @@ describe('chatFunctionClient', () => {
]);
const functions = client.getFunctions();
const adHocInstructions = client.getAdhocInstructions();
const instructions = client.getInstructions();
expect(functions[0]).toEqual({
definition: {
@ -101,7 +101,11 @@ describe('chatFunctionClient', () => {
respond: expect.any(Function),
});
expect(adHocInstructions[0].text).toContain(
expect(
(instructions[0] as RegisterInstructionCallback)({
availableFunctionNames: [GET_DATA_ON_SCREEN_FUNCTION_NAME],
})
).toContain(
dedent(`my_dummy_data: My dummy data
my_other_dummy_data: My other dummy data
`)
@ -134,48 +138,39 @@ describe('chatFunctionClient', () => {
});
});
describe('when adhoc instructions are provided', () => {
describe('when instructions are provided', () => {
let client: ChatFunctionClient;
beforeEach(() => {
client = new ChatFunctionClient([]);
});
describe('register an adhoc Instruction', () => {
it('should register a new adhoc instruction', () => {
const adhocInstruction: AdHocInstruction = {
text: 'Test adhoc instruction',
instruction_type: 'application_instruction',
};
describe('register an Instruction', () => {
it('should register a new instruction', () => {
const instruction = 'Test instruction';
client.registerAdhocInstruction(adhocInstruction);
client.registerInstruction(instruction);
expect(client.getAdhocInstructions()).toContainEqual(adhocInstruction);
expect(client.getInstructions()).toContainEqual(instruction);
});
});
describe('retrieve adHoc instructions', () => {
it('should return all registered adhoc instructions', () => {
const firstAdhocInstruction: AdHocInstruction = {
text: 'First adhoc instruction',
instruction_type: 'application_instruction',
};
describe('retrieve instructions', () => {
it('should return all registered instructions', () => {
const firstInstruction = 'First instruction';
const secondAdhocInstruction: AdHocInstruction = {
text: 'Second adhoc instruction',
instruction_type: 'application_instruction',
};
const secondInstruction = 'Second instruction';
client.registerAdhocInstruction(firstAdhocInstruction);
client.registerAdhocInstruction(secondAdhocInstruction);
client.registerInstruction(firstInstruction);
client.registerInstruction(secondInstruction);
const adhocInstructions = client.getAdhocInstructions();
const instructions = client.getInstructions();
expect(adhocInstructions).toEqual([firstAdhocInstruction, secondAdhocInstruction]);
expect(instructions).toEqual([firstInstruction, secondInstruction]);
});
it('should return an empty array if no adhoc instructions are registered', () => {
const adhocInstructions = client.getAdhocInstructions();
it('should return an empty array if no instructions are registered', () => {
const adhocInstructions = client.getInstructions();
expect(adhocInstructions).toEqual([]);
});

View file

@ -11,18 +11,13 @@ import dedent from 'dedent';
import { compact, keyBy } from 'lodash';
import { Logger } from '@kbn/logging';
import { FunctionVisibility, type FunctionResponse } from '../../../common/functions/types';
import type {
AdHocInstruction,
Message,
ObservabilityAIAssistantScreenContextRequest,
} from '../../../common/types';
import type { Message, ObservabilityAIAssistantScreenContextRequest } from '../../../common/types';
import { filterFunctionDefinitions } from '../../../common/utils/filter_function_definitions';
import type {
FunctionCallChatFunction,
FunctionHandler,
FunctionHandlerRegistry,
InstructionOrCallback,
RegisterAdHocInstruction,
RegisterFunction,
RegisterInstruction,
} from '../types';
@ -41,7 +36,6 @@ export const GET_DATA_ON_SCREEN_FUNCTION_NAME = 'get_data_on_screen';
export class ChatFunctionClient {
private readonly instructions: InstructionOrCallback[] = [];
private readonly adhocInstructions: AdHocInstruction[] = [];
private readonly functionRegistry: FunctionHandlerRegistry = new Map();
private readonly validators: Map<string, ValidateFunction> = new Map();
@ -82,12 +76,13 @@ export class ChatFunctionClient {
}
);
this.registerAdhocInstruction({
text: `The ${GET_DATA_ON_SCREEN_FUNCTION_NAME} function will retrieve specific content from the user's screen by specifying a data key. Use this tool to provide context-aware responses. Available data: ${dedent(
allData.map((data) => `${data.name}: ${data.description}`).join('\n')
)}`,
instruction_type: 'application_instruction',
});
this.registerInstruction(({ availableFunctionNames }) =>
availableFunctionNames.includes(GET_DATA_ON_SCREEN_FUNCTION_NAME)
? `The ${GET_DATA_ON_SCREEN_FUNCTION_NAME} function will retrieve specific content from the user's screen by specifying a data key. Use this tool to provide context-aware responses. Available data: ${dedent(
allData.map((data) => `${data.name}: ${data.description}`).join('\n')
)}`
: undefined
);
}
this.actions.forEach((action) => {
@ -108,10 +103,6 @@ export class ChatFunctionClient {
this.instructions.push(instruction);
};
registerAdhocInstruction: RegisterAdHocInstruction = (instruction: AdHocInstruction) => {
this.adhocInstructions.push(instruction);
};
validate(name: string, parameters: unknown) {
const validator = this.validators.get(name)!;
if (!validator) {
@ -128,10 +119,6 @@ export class ChatFunctionClient {
return this.instructions;
}
getAdhocInstructions(): AdHocInstruction[] {
return this.adhocInstructions;
}
hasAction(name: string) {
return !!this.actions.find((action) => action.name === name)!;
}

View file

@ -130,7 +130,6 @@ describe('Observability AI Assistant client', () => {
getActions: jest.fn(),
validate: jest.fn(),
getInstructions: jest.fn(),
getAdhocInstructions: jest.fn(),
} as any;
let llmSimulator: LlmSimulator;
@ -177,7 +176,6 @@ describe('Observability AI Assistant client', () => {
knowledgeBaseServiceMock.getUserInstructions.mockResolvedValue([]);
functionClientMock.getInstructions.mockReturnValue([EXPECTED_STORED_SYSTEM_MESSAGE]);
functionClientMock.getAdhocInstructions.mockReturnValue([]);
return new ObservabilityAIAssistantClient({
config: {} as ObservabilityAIAssistantConfig,

View file

@ -46,7 +46,7 @@ import {
import { convertMessagesForInference } from '../../../common/convert_messages_for_inference';
import { CompatibleJSONSchema } from '../../../common/functions/types';
import {
type AdHocInstruction,
type Instruction,
type Conversation,
type ConversationCreateRequest,
type ConversationUpdateRequest,
@ -172,7 +172,7 @@ export class ObservabilityAIAssistantClient {
functionClient,
connectorId,
simulateFunctionCalling = false,
instructions: adHocInstructions = [],
userInstructions: apiUserInstructions = [],
messages: initialMessages,
signal,
persist,
@ -191,7 +191,7 @@ export class ObservabilityAIAssistantClient {
title?: string;
isPublic?: boolean;
kibanaPublicUrl?: string;
instructions?: AdHocInstruction[];
userInstructions?: Instruction[];
simulateFunctionCalling?: boolean;
disableFunctions?:
| boolean
@ -207,18 +207,16 @@ export class ObservabilityAIAssistantClient {
const conversationId = persist ? predefinedConversationId || v4() : '';
if (persist && !isConversationUpdate && kibanaPublicUrl) {
adHocInstructions.push({
instruction_type: 'application_instruction',
text: `This conversation will be persisted in Kibana and available at this url: ${
functionClient.registerInstruction(
`This conversation will be persisted in Kibana and available at this url: ${
kibanaPublicUrl + `/app/observabilityAIAssistant/conversations/${conversationId}`
}.`,
});
}.`
);
}
const userInstructions$ = from(this.getKnowledgeBaseUserInstructions()).pipe(shareReplay());
const registeredAdhocInstructions = functionClient.getAdhocInstructions();
const allAdHocInstructions = adHocInstructions.concat(registeredAdhocInstructions);
const kbUserInstructions$ = from(this.getKnowledgeBaseUserInstructions()).pipe(
shareReplay()
);
// if it is:
// - a new conversation
@ -243,12 +241,12 @@ export class ObservabilityAIAssistantClient {
tracer: completeTracer,
}).pipe(shareReplay());
const systemMessage$ = userInstructions$.pipe(
map((userInstructions) => {
const systemMessage$ = kbUserInstructions$.pipe(
map((kbUserInstructions) => {
return getSystemMessageFromInstructions({
applicationInstructions: functionClient.getInstructions(),
userInstructions,
adHocInstructions: allAdHocInstructions,
kbUserInstructions,
apiUserInstructions,
availableFunctionNames: functionClient.getFunctions().map((fn) => fn.definition.name),
});
}),
@ -257,8 +255,8 @@ export class ObservabilityAIAssistantClient {
// we continue the conversation here, after resolving both the materialized
// messages and the knowledge base instructions
const nextEvents$ = forkJoin([systemMessage$, userInstructions$]).pipe(
switchMap(([systemMessage, userInstructions]) => {
const nextEvents$ = forkJoin([systemMessage$, kbUserInstructions$]).pipe(
switchMap(([systemMessage, kbUserInstructions]) => {
// if needed, inject a context function request here
const contextRequest = functionClient.hasFunction(CONTEXT_FUNCTION_NAME)
? getContextFunctionRequestIfNeeded(initialMessages)
@ -285,8 +283,8 @@ export class ObservabilityAIAssistantClient {
// start out with the max number of function calls
functionCallsLeft: MAX_FUNCTION_CALLS,
functionClient,
userInstructions,
adHocInstructions,
kbUserInstructions,
apiUserInstructions,
signal,
logger: this.dependencies.logger,
disableFunctions,

View file

@ -28,7 +28,7 @@ import {
MessageOrChatEvent,
} from '../../../../common/conversation_complete';
import { FunctionVisibility } from '../../../../common/functions/types';
import { AdHocInstruction, Instruction } from '../../../../common/types';
import { Instruction } from '../../../../common/types';
import { createFunctionResponseMessage } from '../../../../common/utils/create_function_response_message';
import { emitWithConcatenatedMessage } from '../../../../common/utils/emit_with_concatenated_message';
import type { ChatFunctionClient } from '../../chat_function_client';
@ -173,8 +173,8 @@ export function continueConversation({
chat,
signal,
functionCallsLeft,
adHocInstructions = [],
userInstructions,
apiUserInstructions = [],
kbUserInstructions,
logger,
disableFunctions,
tracer,
@ -186,8 +186,8 @@ export function continueConversation({
chat: AutoAbortedChatFunction;
signal: AbortSignal;
functionCallsLeft: number;
adHocInstructions: AdHocInstruction[];
userInstructions: Instruction[];
apiUserInstructions: Instruction[];
kbUserInstructions: Instruction[];
logger: Logger;
disableFunctions:
| boolean
@ -323,8 +323,8 @@ export function continueConversation({
functionCallsLeft: nextFunctionCallsLeft,
functionClient,
signal,
userInstructions,
adHocInstructions,
kbUserInstructions,
apiUserInstructions,
logger,
disableFunctions,
tracer,

View file

@ -14,11 +14,7 @@ import type {
FunctionDefinition,
FunctionResponse,
} from '../../common/functions/types';
import type {
Message,
ObservabilityAIAssistantScreenContextRequest,
AdHocInstruction,
} from '../../common/types';
import type { Message, ObservabilityAIAssistantScreenContextRequest } from '../../common/types';
import type { ObservabilityAIAssistantRouteHandlerResources } from '../routes/types';
import { ChatFunctionClient } from './chat_function_client';
import type { ObservabilityAIAssistantClient } from './client';
@ -76,8 +72,6 @@ export type RegisterInstructionCallback = ({
export type RegisterInstruction = (...instruction: InstructionOrCallback[]) => void;
export type RegisterAdHocInstruction = (...instruction: AdHocInstruction[]) => void;
export type RegisterFunction = <
TParameters extends CompatibleJSONSchema = any,
TResponse extends FunctionResponse = any,

View file

@ -14,8 +14,8 @@ describe('getSystemMessageFromInstructions', () => {
expect(
getSystemMessageFromInstructions({
applicationInstructions: ['first', 'second'],
userInstructions: [],
adHocInstructions: [],
kbUserInstructions: [],
apiUserInstructions: [],
availableFunctionNames: [],
})
).toEqual(`first\n\nsecond`);
@ -30,8 +30,8 @@ describe('getSystemMessageFromInstructions', () => {
return availableFunctionNames[0];
},
],
userInstructions: [],
adHocInstructions: [],
kbUserInstructions: [],
apiUserInstructions: [],
availableFunctionNames: ['myFunction'],
})
).toEqual(`first\n\nmyFunction`);
@ -41,12 +41,11 @@ describe('getSystemMessageFromInstructions', () => {
expect(
getSystemMessageFromInstructions({
applicationInstructions: ['first'],
userInstructions: [{ id: 'second', text: 'second from kb' }],
adHocInstructions: [
kbUserInstructions: [{ id: 'second', text: 'second from kb' }],
apiUserInstructions: [
{
id: 'second',
text: 'second from adhoc instruction',
instruction_type: 'user_instruction',
},
],
availableFunctionNames: [],
@ -58,8 +57,8 @@ describe('getSystemMessageFromInstructions', () => {
expect(
getSystemMessageFromInstructions({
applicationInstructions: ['first'],
userInstructions: [{ id: 'second', text: 'second_kb' }],
adHocInstructions: [],
kbUserInstructions: [{ id: 'second', text: 'second_kb' }],
apiUserInstructions: [],
availableFunctionNames: [],
})
).toEqual(`first\n\n${USER_INSTRUCTIONS_HEADER}\n\nsecond_kb`);
@ -74,8 +73,8 @@ describe('getSystemMessageFromInstructions', () => {
return undefined;
},
],
userInstructions: [],
adHocInstructions: [],
kbUserInstructions: [],
apiUserInstructions: [],
availableFunctionNames: [],
})
).toEqual(`first`);

View file

@ -5,9 +5,8 @@
* 2.0.
*/
import { compact, partition, uniqBy } from 'lodash';
import { v4 } from 'uuid';
import { AdHocInstruction, Instruction } from '../../../common/types';
import { compact, uniqBy } from 'lodash';
import { Instruction } from '../../../common/types';
import { withTokenBudget } from '../../../common/utils/with_token_budget';
import { InstructionOrCallback } from '../types';
@ -22,16 +21,16 @@ export function getSystemMessageFromInstructions({
// application instructions registered by the functions. These will be displayed first
applicationInstructions,
// instructions provided by the user. These will be displayed after the application instructions and only if they fit within the token budget
userInstructions: kbUserInstructions,
// instructions provided by the user via the KB. These will be displayed after the application instructions and only if they fit within the token budget
kbUserInstructions,
// ad-hoc instruction. Can be either user or application instruction
adHocInstructions,
// instructions provided by the user via the API. These will be displayed after the application instructions and only if they fit within the token budget
apiUserInstructions,
availableFunctionNames,
}: {
applicationInstructions: InstructionOrCallback[];
userInstructions: Instruction[];
adHocInstructions: AdHocInstruction[];
kbUserInstructions: Instruction[];
apiUserInstructions: Instruction[];
availableFunctionNames: string[];
}): string {
const allApplicationInstructions = compact(
@ -43,28 +42,16 @@ export function getSystemMessageFromInstructions({
})
);
const adHocInstructionsWithId = adHocInstructions.map((adHocInstruction) => ({
...adHocInstruction,
id: adHocInstruction?.id ?? v4(),
}));
// split ad hoc instructions into user instructions and application instructions
const [adHocUserInstructions, adHocApplicationInstructions] = partition(
adHocInstructionsWithId,
(instruction) => instruction.instruction_type === 'user_instruction'
);
// all adhoc instructions and KB instructions.
// adhoc instructions will be prioritized over Knowledge Base instructions if the id is the same
// all api user instructions and KB instructions.
// api instructions will be prioritized over Knowledge Base instructions if the id is the same
const allUserInstructions = withTokenBudget(
uniqBy([...adHocUserInstructions, ...kbUserInstructions], (i) => i.id),
uniqBy([...apiUserInstructions, ...kbUserInstructions], (i) => i.id),
1000
);
return [
// application instructions
...allApplicationInstructions,
...adHocApplicationInstructions,
// user instructions
...(allUserInstructions.length ? [USER_INSTRUCTIONS_HEADER, ...allUserInstructions] : []),

View file

@ -113,7 +113,7 @@ describe('observabilityAIAssistant rule_connector', () => {
getFunctionClient: async () => ({
getFunctions: () => [],
getInstructions: () => [],
getAdhocInstructions: () => [],
registerInstruction: () => [],
}),
},
context: {},

View file

@ -34,12 +34,12 @@ import {
import { concatenateChatCompletionChunks } from '@kbn/observability-ai-assistant-plugin/common/utils/concatenate_chat_completion_chunks';
import { CompatibleJSONSchema } from '@kbn/observability-ai-assistant-plugin/common/functions/types';
import { AlertDetailsContextualInsightsService } from '@kbn/observability-plugin/server/services';
import { AdHocInstruction } from '@kbn/observability-ai-assistant-plugin/common/types';
import { EXECUTE_CONNECTOR_FUNCTION_NAME } from '@kbn/observability-ai-assistant-plugin/server/functions/execute_connector';
import { ObservabilityAIAssistantClient } from '@kbn/observability-ai-assistant-plugin/server';
import { ChatFunctionClient } from '@kbn/observability-ai-assistant-plugin/server/service/chat_function_client';
import { ActionsClient } from '@kbn/actions-plugin/server';
import { PublicMethodsOf } from '@kbn/utility-types';
import { RegisterInstructionCallback } from '@kbn/observability-ai-assistant-plugin/server/service/types';
import { convertSchemaToOpenApi } from './convert_schema_to_open_api';
import { OBSERVABILITY_AI_ASSISTANT_CONNECTOR_ID } from '../../common/rule_connector';
import { ALERT_STATUSES } from '../../common/constants';
@ -253,32 +253,32 @@ async function executeAlertsChatCompletion(
});
});
const backgroundInstruction: AdHocInstruction = {
instruction_type: 'application_instruction',
text: dedent(
`You are called as a background process because alerts have changed state.
const backgroundInstruction = dedent(
`You are called as a background process because alerts have changed state.
As a background process you are not interacting with a user. Because of that DO NOT ask for user
input if tasked to execute actions. You can generate multiple responses in a row.
If available, include the link of the conversation at the end of your answer.`
),
};
);
functionClient.registerInstruction(backgroundInstruction);
const hasSlackConnector = !!connectorsList.filter(
(connector) => connector.actionTypeId === '.slack'
).length;
if (hasSlackConnector && functionClient.hasFunction(EXECUTE_CONNECTOR_FUNCTION_NAME)) {
const slackConnectorInstruction: AdHocInstruction = {
instruction_type: 'application_instruction',
text: dedent(
`The execute_connector function can be used to invoke Kibana connectors.
if (hasSlackConnector) {
const slackConnectorInstruction: RegisterInstructionCallback = ({ availableFunctionNames }) =>
availableFunctionNames.includes(EXECUTE_CONNECTOR_FUNCTION_NAME)
? dedent(
`The execute_connector function can be used to invoke Kibana connectors.
To send to the Slack connector, you need the following arguments:
- the "id" of the connector
- the "params" parameter that you will fill with the message
Please include both "id" and "params.message" in the function arguments when executing the Slack connector..`
),
};
functionClient.registerAdhocInstruction(slackConnectorInstruction);
)
: undefined;
functionClient.registerInstruction(slackConnectorInstruction);
}
const alertsContext = await getAlertsContext(
@ -312,7 +312,6 @@ If available, include the link of the conversation at the end of your answer.`
connectorId: params.connector,
signal: new AbortController().signal,
kibanaPublicUrl: (await resources.plugins.core.start()).http.basePath.publicBaseUrl,
instructions: [backgroundInstruction],
messages: [
{
'@timestamp': new Date().toISOString(),

View file

@ -14,7 +14,7 @@ import {
MessageAddEvent,
type StreamingChatResponseEvent,
} from '@kbn/observability-ai-assistant-plugin/common/conversation_complete';
import { type AdHocInstruction } from '@kbn/observability-ai-assistant-plugin/common/types';
import { type Instruction } from '@kbn/observability-ai-assistant-plugin/common/types';
import type { ChatCompletionChunkToolCall } from '@kbn/inference-common';
import { ChatCompletionStreamParams } from 'openai/lib/ChatCompletionStream';
import {
@ -51,7 +51,7 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon
conversationResponse,
}: {
actions?: Array<Pick<FunctionDefinition, 'name' | 'description' | 'parameters'>>;
instructions?: AdHocInstruction[];
instructions?: Array<string | Instruction>;
format?: 'openai' | 'default';
conversationResponse: string | ToolMessage;
}) {
@ -160,12 +160,7 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon
before(async () => {
const { conversationSimulator } = await addInterceptorsAndCallComplete({
instructions: [
{
text: 'This is a random instruction',
instruction_type: 'user_instruction',
},
],
instructions: ['This is a random instruction'],
actions: [action],
conversationResponse: {
tool_calls: [toolCallMock],