Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
504fce5
Add internal endpoints for KI feature identification
cesco-f Apr 15, 2026
efd69dc
Refactor features identification task to use shared service
cesco-f Apr 15, 2026
4857b58
Simplify API surface and improve shared service
cesco-f Apr 15, 2026
ef37bd1
Add runId to IterationResult for run traceability
cesco-f Apr 15, 2026
3849668
fix(error): better error handling
cesco-f Apr 16, 2026
48d1761
Split features_identification_service into focused modules
cesco-f Apr 16, 2026
493ced3
Derive IterationResult from Zod schema, add route failure telemetry, …
cesco-f Apr 16, 2026
5e239f7
Remove redundant iterationResult from return type, fix failure teleme…
cesco-f Apr 16, 2026
0614a3f
Rename path param to streamName and make request body optional
cesco-f Apr 16, 2026
e024b20
fix(cr): code review
cesco-f Apr 16, 2026
f182a2d
Merge branch 'main' into features-identification-endpoints
cesco-f Apr 16, 2026
65e8d5f
Merge branch 'main' into features-identification-endpoints
cesco-f Apr 18, 2026
57cfbce
Merge branch 'main' into features-identification-endpoints
cesco-f Apr 20, 2026
915e83d
Store run_id on features to avoid passing internal state through the API
cesco-f Apr 21, 2026
ed0e9ae
Merge branch 'main' into features-identification-endpoints
cesco-f Apr 21, 2026
598a0e8
Merge branch 'main' into features-identification-endpoints
cesco-f Apr 21, 2026
ba66a5b
Merge branch 'main' into features-identification-endpoints
cesco-f Apr 21, 2026
144c5af
Add run_id to computed features for audit trail parity
cesco-f Apr 22, 2026
f156864
Merge branch 'main' into features-identification-endpoints
cesco-f Apr 22, 2026
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 @@ -260,6 +260,7 @@ export { TaskStatus, type TaskResult } from './src/tasks/types';

export type { GenerateDescriptionResult } from './src/api/description_generation';
export type { IdentifyFeaturesResult, IterationResult } from './src/api/features';
export { tokenCountSchema, iterationResultSchema } from './src/api/features';

export {
type GenerateInsightsResult,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,34 @@
* 2.0.
*/

import { z } from '@kbn/zod/v4';
import type { ChatCompletionTokenCount } from '@kbn/inference-common';
import type { BaseFeature } from '../../feature';

export interface IterationResult {
iteration: number;
durationMs: number;
state: 'success' | 'failure';
tokensUsed: ChatCompletionTokenCount;
newFeatures: Array<{ id: string; title: string }>;
updatedFeatures: Array<{ id: string; title: string }>;
}
export const tokenCountSchema = z.object({
prompt: z.number(),
completion: z.number(),
thinking: z.number().optional(),
total: z.number(),
cached: z.number().optional(),
});

const featureSummarySchema = z.object({
id: z.string(),
title: z.string(),
});

export const iterationResultSchema = z.object({
runId: z.string(),
iteration: z.number(),
durationMs: z.number(),
state: z.enum(['success', 'failure']),
tokensUsed: tokenCountSchema,
newFeatures: z.array(featureSummarySchema),
updatedFeatures: z.array(featureSummarySchema),
});

export type IterationResult = z.infer<typeof iterationResultSchema>;

export interface IdentifyFeaturesResult {
features: BaseFeature[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export const featureSchema = baseFeatureSchema.and(
last_seen: z.string(),
expires_at: z.string().optional(),
excluded_at: z.string().optional(),
run_id: z.string().optional(),
})
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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 type { ElasticsearchClient } from '@kbn/core/server';
import type { Logger } from '@kbn/logging';
import type { Feature, Streams } from '@kbn/streams-schema';
import { generateAllComputedFeatures } from '@kbn/streams-ai';
import type { FeatureClient } from '../../streams/feature/feature_client';
import { reconcileComputedFeatures } from './reconcile_features';

export interface IdentifyComputedFeaturesOptions {
stream: Streams.all.Definition;
streamName: string;
start: number;
end: number;
esClient: ElasticsearchClient;
featureClient: FeatureClient;
logger: Logger;
featureTtlDays?: number;
runId: string;
}

export async function identifyComputedFeatures({
stream,
streamName,
start,
end,
esClient,
featureClient,
logger,
featureTtlDays,
runId,
}: IdentifyComputedFeaturesOptions): Promise<Feature[]> {
const computedFeatures = await generateAllComputedFeatures({
stream,
start,
end,
esClient,
logger: logger.get('computed_features'),
});

const reconciledComputedFeatures = reconcileComputedFeatures({
computedFeatures,
streamName,
featureTtlDays,
runId,
});

if (reconciledComputedFeatures.length > 0) {
await featureClient.bulk(
streamName,
reconciledComputedFeatures.map((feature) => ({ index: { feature } }))
);
}

return reconciledComputedFeatures;
}
Loading
Loading