Skip to content

Commit f9f0cce

Browse files
authored
Merge pull request #122 from ethpandaops/refactor/ai-assertion-gen
refactor: fix block number range estimation and migrate to ai.Engine
2 parents 5ee992c + 1b733c3 commit f9f0cce

File tree

5 files changed

+356
-289
lines changed

5 files changed

+356
-289
lines changed

pkg/cc/api_stack.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -322,12 +322,12 @@ func (a *apiHandler) getStackStatusData() stackStatusResponse {
322322
// from another stack), report "stopped" so we don't confuse the frontend.
323323
status := stackStatusStopped
324324

325-
switch {
326-
case currentStatus == stackStatusStarting:
325+
switch currentStatus {
326+
case stackStatusStarting:
327327
status = stackStatusStarting
328-
case currentStatus == stackStatusStopping:
328+
case stackStatusStopping:
329329
status = stackStatusStopping
330-
case currentStatus == stackStatusRunning:
330+
case stackStatusRunning:
331331
status = stackStatusRunning
332332
}
333333

pkg/commands/lab_xatu_cbt_generate_transformation.go

Lines changed: 151 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99
"sync"
1010

11+
"github.com/ethpandaops/xcli/pkg/ai"
1112
"github.com/ethpandaops/xcli/pkg/config"
1213
"github.com/ethpandaops/xcli/pkg/constants"
1314
"github.com/ethpandaops/xcli/pkg/seeddata"
@@ -307,112 +308,117 @@ func runGenerateTransformationTest(
307308
return fmt.Errorf("failed to detect range columns: %w", err)
308309
}
309310

310-
discoveryResult, err = seeddata.FallbackRangeDiscovery(ctx, gen, externalModels, network, rangeInfos, duration, labCfg.Repos.XatuCBT)
311-
if err != nil {
312-
return fmt.Errorf("fallback range discovery failed: %w", err)
313-
}
314-
} else if discoveryClient, discoveryErr := seeddata.NewClaudeDiscoveryClient(log, gen); discoveryErr != nil {
315-
ui.Warning(fmt.Sprintf("Claude CLI not available: %v", discoveryErr))
316-
ui.Info("Falling back to heuristic range detection")
317-
318-
// Fallback to heuristic detection
319-
var rangeInfos map[string]*seeddata.RangeColumnInfo
320-
321-
rangeInfos, err = seeddata.DetectRangeColumnsForModels(externalModels, labCfg.Repos.XatuCBT)
322-
if err != nil {
323-
return fmt.Errorf("failed to detect range columns: %w", err)
324-
}
325-
326311
discoveryResult, err = seeddata.FallbackRangeDiscovery(ctx, gen, externalModels, network, rangeInfos, duration, labCfg.Repos.XatuCBT)
327312
if err != nil {
328313
return fmt.Errorf("fallback range discovery failed: %w", err)
329314
}
330315
} else {
331-
// Gather schema information
332-
schemaSpinner := ui.NewSpinner("Gathering schema information")
316+
engine, engineErr := ai.NewEngine(ai.DefaultProvider, log)
333317

334-
schemaInfo, schemaErr := discoveryClient.GatherSchemaInfo(ctx, externalModels, network, labCfg.Repos.XatuCBT)
335-
if schemaErr != nil {
336-
schemaSpinner.Fail("Failed to gather schema info")
318+
discoveryClient := seeddata.NewClaudeDiscoveryClient(log, gen, engine)
319+
if engineErr != nil || !discoveryClient.IsAvailable() {
320+
ui.Warning("AI engine not available")
321+
ui.Info("Falling back to heuristic range detection")
337322

338-
return fmt.Errorf("failed to gather schema info: %w", schemaErr)
339-
}
323+
// Fallback to heuristic detection
324+
var rangeInfos map[string]*seeddata.RangeColumnInfo
340325

341-
schemaSpinner.Success(fmt.Sprintf("Schema info gathered for %d models", len(schemaInfo)))
326+
rangeInfos, err = seeddata.DetectRangeColumnsForModels(externalModels, labCfg.Repos.XatuCBT)
327+
if err != nil {
328+
return fmt.Errorf("failed to detect range columns: %w", err)
329+
}
342330

343-
// Display detected range info
344-
for _, schema := range schemaInfo {
345-
if schema.RangeInfo != nil {
346-
status := "detected"
347-
if !schema.RangeInfo.Detected {
348-
status = "default"
349-
}
331+
discoveryResult, err = seeddata.FallbackRangeDiscovery(ctx, gen, externalModels, network, rangeInfos, duration, labCfg.Repos.XatuCBT)
332+
if err != nil {
333+
return fmt.Errorf("fallback range discovery failed: %w", err)
334+
}
335+
} else {
336+
// Gather schema information
337+
schemaSpinner := ui.NewSpinner("Gathering schema information")
350338

351-
rangeStr := ""
352-
if schema.RangeInfo.MinValue != "" && schema.RangeInfo.MaxValue != "" {
353-
rangeStr = fmt.Sprintf(" [%s → %s]", schema.RangeInfo.MinValue, schema.RangeInfo.MaxValue)
354-
}
339+
schemaInfo, schemaErr := discoveryClient.GatherSchemaInfo(ctx, externalModels, network, labCfg.Repos.XatuCBT)
340+
if schemaErr != nil {
341+
schemaSpinner.Fail("Failed to gather schema info")
355342

356-
ui.Info(fmt.Sprintf(" • %s: %s (%s)%s", schema.Model, schema.RangeInfo.Column, status, rangeStr))
343+
return fmt.Errorf("failed to gather schema info: %w", schemaErr)
357344
}
358-
}
359345

360-
// Read transformation SQL
361-
transformationSQL, sqlErr := seeddata.ReadTransformationSQL(model, labCfg.Repos.XatuCBT)
362-
if sqlErr != nil {
363-
return fmt.Errorf("failed to read transformation SQL: %w", sqlErr)
364-
}
346+
schemaSpinner.Success(fmt.Sprintf("Schema info gathered for %d models", len(schemaInfo)))
365347

366-
// Read intermediate dependency SQL (for WHERE clause analysis)
367-
intermediateSQL, intErr := seeddata.ReadIntermediateSQL(tree, labCfg.Repos.XatuCBT)
368-
if intErr != nil {
369-
ui.Warning(fmt.Sprintf("Could not read intermediate SQL: %v", intErr))
370-
// Continue without intermediate SQL - not critical
371-
}
348+
// Display detected range info
349+
for _, schema := range schemaInfo {
350+
if schema.RangeInfo != nil {
351+
status := "detected"
352+
if !schema.RangeInfo.Detected {
353+
status = "default"
354+
}
372355

373-
// Convert to IntermediateSQL slice
374-
var intermediateModels []seeddata.IntermediateSQL
375-
for modelName, sql := range intermediateSQL {
376-
intermediateModels = append(intermediateModels, seeddata.IntermediateSQL{
377-
Model: modelName,
378-
SQL: sql,
379-
})
380-
}
356+
rangeStr := ""
357+
if schema.RangeInfo.MinValue != "" && schema.RangeInfo.MaxValue != "" {
358+
rangeStr = fmt.Sprintf(" [%s → %s]", schema.RangeInfo.MinValue, schema.RangeInfo.MaxValue)
359+
}
381360

382-
if len(intermediateModels) > 0 {
383-
ui.Info(fmt.Sprintf("Including %d intermediate model(s) for WHERE clause analysis", len(intermediateModels)))
384-
}
361+
ui.Info(fmt.Sprintf(" • %s: %s (%s)%s", schema.Model, schema.RangeInfo.Column, status, rangeStr))
362+
}
363+
}
385364

386-
// Invoke Claude for analysis
387-
ui.Blank()
365+
// Read transformation SQL
366+
transformationSQL, sqlErr := seeddata.ReadTransformationSQL(model, labCfg.Repos.XatuCBT)
367+
if sqlErr != nil {
368+
return fmt.Errorf("failed to read transformation SQL: %w", sqlErr)
369+
}
388370

389-
analysisSpinner := ui.NewSpinner("Analyzing correlation strategy with Claude")
371+
// Read intermediate dependency SQL (for WHERE clause analysis)
372+
intermediateSQL, intErr := seeddata.ReadIntermediateSQL(tree, labCfg.Repos.XatuCBT)
373+
if intErr != nil {
374+
ui.Warning(fmt.Sprintf("Could not read intermediate SQL: %v", intErr))
375+
// Continue without intermediate SQL - not critical
376+
}
390377

391-
discoveryResult, err = discoveryClient.AnalyzeRanges(ctx, seeddata.DiscoveryInput{
392-
TransformationModel: model,
393-
TransformationSQL: transformationSQL,
394-
IntermediateModels: intermediateModels,
395-
Network: network,
396-
Duration: duration,
397-
ExternalModels: schemaInfo,
398-
})
399-
if err != nil {
400-
analysisSpinner.Fail("AI analysis failed")
401-
ui.Warning(fmt.Sprintf("Claude analysis failed: %v", err))
402-
ui.Info("Falling back to heuristic range detection")
378+
// Convert to IntermediateSQL slice
379+
var intermediateModels []seeddata.IntermediateSQL
380+
for modelName, sql := range intermediateSQL {
381+
intermediateModels = append(intermediateModels, seeddata.IntermediateSQL{
382+
Model: modelName,
383+
SQL: sql,
384+
})
385+
}
403386

404-
// Fallback to heuristic detection
405-
rangeInfos, rangeErr := seeddata.DetectRangeColumnsForModels(externalModels, labCfg.Repos.XatuCBT)
406-
if rangeErr != nil {
407-
return fmt.Errorf("failed to detect range columns: %w", rangeErr)
387+
if len(intermediateModels) > 0 {
388+
ui.Info(fmt.Sprintf("Including %d intermediate model(s) for WHERE clause analysis", len(intermediateModels)))
408389
}
409390

410-
discoveryResult, err = seeddata.FallbackRangeDiscovery(ctx, gen, externalModels, network, rangeInfos, duration, labCfg.Repos.XatuCBT)
391+
// Invoke Claude for analysis
392+
ui.Blank()
393+
394+
analysisSpinner := ui.NewSpinner("Analyzing correlation strategy with Claude")
395+
396+
discoveryResult, err = discoveryClient.AnalyzeRanges(ctx, seeddata.DiscoveryInput{
397+
TransformationModel: model,
398+
TransformationSQL: transformationSQL,
399+
IntermediateModels: intermediateModels,
400+
Network: network,
401+
Duration: duration,
402+
ExternalModels: schemaInfo,
403+
})
411404
if err != nil {
412-
return fmt.Errorf("fallback range discovery failed: %w", err)
405+
analysisSpinner.Fail("AI analysis failed")
406+
ui.Warning(fmt.Sprintf("Claude analysis failed: %v", err))
407+
ui.Info("Falling back to heuristic range detection")
408+
409+
// Fallback to heuristic detection
410+
rangeInfos, rangeErr := seeddata.DetectRangeColumnsForModels(externalModels, labCfg.Repos.XatuCBT)
411+
if rangeErr != nil {
412+
return fmt.Errorf("failed to detect range columns: %w", rangeErr)
413+
}
414+
415+
discoveryResult, err = seeddata.FallbackRangeDiscovery(ctx, gen, externalModels, network, rangeInfos, duration, labCfg.Repos.XatuCBT)
416+
if err != nil {
417+
return fmt.Errorf("fallback range discovery failed: %w", err)
418+
}
419+
} else {
420+
analysisSpinner.Success(fmt.Sprintf("Strategy generated (confidence: %.0f%%)", discoveryResult.OverallConfidence*100))
413421
}
414-
} else {
415-
analysisSpinner.Success(fmt.Sprintf("Strategy generated (confidence: %.0f%%)", discoveryResult.OverallConfidence*100))
416422
}
417423
}
418424

@@ -995,11 +1001,18 @@ func promptForTransformationModel(xatuCBTPath string) (string, error) {
9951001
func generateAIAssertions(ctx context.Context, log logrus.FieldLogger, model string, externalModels []string, xatuCBTPath string) ([]seeddata.Assertion, error) {
9961002
aiSpinner := ui.NewSpinner("Analyzing transformation SQL with Claude")
9971003

998-
client, err := seeddata.NewClaudeAssertionClient(log)
999-
if err != nil {
1000-
aiSpinner.Fail("Claude CLI not available")
1004+
engine, engineErr := ai.NewEngine(ai.DefaultProvider, log)
1005+
if engineErr != nil {
1006+
aiSpinner.Fail("AI engine not available")
1007+
1008+
return nil, fmt.Errorf("AI engine not available: %w", engineErr)
1009+
}
10011010

1002-
return nil, fmt.Errorf("claude CLI not available: %w", err)
1011+
client := seeddata.NewClaudeAssertionClient(log, engine)
1012+
if !client.IsAvailable() {
1013+
aiSpinner.Fail("AI engine not available")
1014+
1015+
return nil, fmt.Errorf("AI engine is not available")
10031016
}
10041017

10051018
// Read transformation SQL
@@ -1185,58 +1198,17 @@ func runSingleModelGeneration(
11851198
return fmt.Errorf("failed to detect range columns: %w", rangeErr)
11861199
}
11871200

1188-
discoveryResult, err = seeddata.FallbackRangeDiscovery(ctx, gen, externalModels, network, rangeInfos, duration, labCfg.Repos.XatuCBT)
1189-
if err != nil {
1190-
return fmt.Errorf("fallback range discovery failed: %w", err)
1191-
}
1192-
} else if discoveryClient, discoveryErr := seeddata.NewClaudeDiscoveryClient(log, gen); discoveryErr != nil {
1193-
// Claude not available, use heuristics
1194-
logInfo("Claude unavailable, using heuristic range detection")
1195-
1196-
rangeInfos, rangeErr := seeddata.DetectRangeColumnsForModels(externalModels, labCfg.Repos.XatuCBT)
1197-
if rangeErr != nil {
1198-
return fmt.Errorf("failed to detect range columns: %w", rangeErr)
1199-
}
1200-
12011201
discoveryResult, err = seeddata.FallbackRangeDiscovery(ctx, gen, externalModels, network, rangeInfos, duration, labCfg.Repos.XatuCBT)
12021202
if err != nil {
12031203
return fmt.Errorf("fallback range discovery failed: %w", err)
12041204
}
12051205
} else {
1206-
// Use Claude for analysis
1207-
logInfo("Analyzing with Claude AI")
1208-
1209-
schemaInfo, schemaErr := discoveryClient.GatherSchemaInfo(ctx, externalModels, network, labCfg.Repos.XatuCBT)
1210-
if schemaErr != nil {
1211-
return fmt.Errorf("failed to gather schema info: %w", schemaErr)
1212-
}
1213-
1214-
transformationSQL, sqlErr := seeddata.ReadTransformationSQL(model, labCfg.Repos.XatuCBT)
1215-
if sqlErr != nil {
1216-
return fmt.Errorf("failed to read transformation SQL: %w", sqlErr)
1217-
}
1218-
1219-
intermediateSQL, _ := seeddata.ReadIntermediateSQL(tree, labCfg.Repos.XatuCBT)
1220-
1221-
var intermediateModels []seeddata.IntermediateSQL
1222-
for modelName, sql := range intermediateSQL {
1223-
intermediateModels = append(intermediateModels, seeddata.IntermediateSQL{
1224-
Model: modelName,
1225-
SQL: sql,
1226-
})
1227-
}
1206+
engine, engineErr := ai.NewEngine(ai.DefaultProvider, log)
12281207

1229-
discoveryResult, err = discoveryClient.AnalyzeRanges(ctx, seeddata.DiscoveryInput{
1230-
TransformationModel: model,
1231-
TransformationSQL: transformationSQL,
1232-
IntermediateModels: intermediateModels,
1233-
Network: network,
1234-
Duration: duration,
1235-
ExternalModels: schemaInfo,
1236-
})
1237-
if err != nil {
1238-
// Fallback to heuristics
1239-
logInfo("Claude analysis failed, falling back to heuristics")
1208+
discoveryClient := seeddata.NewClaudeDiscoveryClient(log, gen, engine)
1209+
if engineErr != nil || !discoveryClient.IsAvailable() {
1210+
// AI not available, use heuristics
1211+
logInfo("AI engine unavailable, using heuristic range detection")
12401212

12411213
rangeInfos, rangeErr := seeddata.DetectRangeColumnsForModels(externalModels, labCfg.Repos.XatuCBT)
12421214
if rangeErr != nil {
@@ -1248,7 +1220,53 @@ func runSingleModelGeneration(
12481220
return fmt.Errorf("fallback range discovery failed: %w", err)
12491221
}
12501222
} else {
1251-
logInfo(fmt.Sprintf("Claude strategy generated (confidence: %.0f%%)", discoveryResult.OverallConfidence*100))
1223+
// Use AI for analysis
1224+
logInfo("Analyzing with Claude AI")
1225+
1226+
schemaInfo, schemaErr := discoveryClient.GatherSchemaInfo(ctx, externalModels, network, labCfg.Repos.XatuCBT)
1227+
if schemaErr != nil {
1228+
return fmt.Errorf("failed to gather schema info: %w", schemaErr)
1229+
}
1230+
1231+
transformationSQL, sqlErr := seeddata.ReadTransformationSQL(model, labCfg.Repos.XatuCBT)
1232+
if sqlErr != nil {
1233+
return fmt.Errorf("failed to read transformation SQL: %w", sqlErr)
1234+
}
1235+
1236+
intermediateSQL, _ := seeddata.ReadIntermediateSQL(tree, labCfg.Repos.XatuCBT)
1237+
1238+
var intermediateModels []seeddata.IntermediateSQL
1239+
for modelName, sql := range intermediateSQL {
1240+
intermediateModels = append(intermediateModels, seeddata.IntermediateSQL{
1241+
Model: modelName,
1242+
SQL: sql,
1243+
})
1244+
}
1245+
1246+
discoveryResult, err = discoveryClient.AnalyzeRanges(ctx, seeddata.DiscoveryInput{
1247+
TransformationModel: model,
1248+
TransformationSQL: transformationSQL,
1249+
IntermediateModels: intermediateModels,
1250+
Network: network,
1251+
Duration: duration,
1252+
ExternalModels: schemaInfo,
1253+
})
1254+
if err != nil {
1255+
// Fallback to heuristics
1256+
logInfo("Claude analysis failed, falling back to heuristics")
1257+
1258+
rangeInfos, rangeErr := seeddata.DetectRangeColumnsForModels(externalModels, labCfg.Repos.XatuCBT)
1259+
if rangeErr != nil {
1260+
return fmt.Errorf("failed to detect range columns: %w", rangeErr)
1261+
}
1262+
1263+
discoveryResult, err = seeddata.FallbackRangeDiscovery(ctx, gen, externalModels, network, rangeInfos, duration, labCfg.Repos.XatuCBT)
1264+
if err != nil {
1265+
return fmt.Errorf("fallback range discovery failed: %w", err)
1266+
}
1267+
} else {
1268+
logInfo(fmt.Sprintf("Claude strategy generated (confidence: %.0f%%)", discoveryResult.OverallConfidence*100))
1269+
}
12521270
}
12531271
}
12541272

0 commit comments

Comments
 (0)