Skip to content

Commit fc31b92

Browse files
kulvirgitclaude
andcommitted
feat: dbt integration improvements — lineage, profiles tool, + operator
- `dbt.lineage` bridge method: takes manifest path + model name, extracts compiled SQL + upstream schemas, computes column-level lineage - `dbt_profiles` TS tool: exposes existing `dbt.profiles` bridge method so the agent can discover warehouse connections from profiles.yml - `dbt_lineage` TS tool: agent-facing tool for dbt model lineage - Auto-prepend `+` to dbt selectors for build/run/test commands to always include upstream dependencies (user preference) - `lineage_check` tool: expose `schema_context` param (was in protocol but not in the tool definition) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 14022f3 commit fc31b92

9 files changed

Lines changed: 410 additions & 1 deletion

File tree

packages/altimate-code/src/bridge/protocol.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,25 @@ export interface SqlGuardIsSafeParams {
904904
sql: string
905905
}
906906

907+
// --- dbt Lineage ---
908+
909+
export interface DbtLineageParams {
910+
manifest_path: string
911+
model: string
912+
dialect?: string
913+
}
914+
915+
export interface DbtLineageResult {
916+
model_name: string
917+
model_unique_id?: string
918+
compiled_sql?: string
919+
edges: LineageEdge[]
920+
tables: string[]
921+
columns: string[]
922+
confidence: string
923+
confidence_factors: string[]
924+
}
925+
907926
// --- dbt Profile Discovery ---
908927

909928
export interface DbtProfilesParams {
@@ -982,6 +1001,7 @@ export const BridgeMethods = {
9821001
"lineage.check": {} as { params: LineageCheckParams; result: LineageCheckResult },
9831002
"dbt.run": {} as { params: DbtRunParams; result: DbtRunResult },
9841003
"dbt.manifest": {} as { params: DbtManifestParams; result: DbtManifestResult },
1004+
"dbt.lineage": {} as { params: DbtLineageParams; result: DbtLineageResult },
9851005
"warehouse.list": {} as { params: WarehouseListParams; result: WarehouseListResult },
9861006
"warehouse.test": {} as { params: WarehouseTestParams; result: WarehouseTestResult },
9871007
"finops.query_history": {} as { params: QueryHistoryParams; result: QueryHistoryResult },
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import z from "zod"
2+
import { Tool } from "./tool"
3+
import { Bridge } from "../bridge/client"
4+
import type { DbtLineageResult } from "../bridge/protocol"
5+
6+
export const DbtLineageTool = Tool.define("dbt_lineage", {
7+
description:
8+
"Compute column-level lineage for a dbt model. Takes a manifest.json path and model name, extracts compiled SQL and upstream schemas, and traces how source columns flow to output columns.",
9+
parameters: z.object({
10+
manifest_path: z.string().describe("Path to dbt manifest.json file"),
11+
model: z.string().describe("Model name or unique_id (e.g. 'my_model' or 'model.project.my_model')"),
12+
dialect: z.string().optional().describe("SQL dialect override (auto-detected from manifest if omitted)"),
13+
}),
14+
async execute(args, ctx) {
15+
try {
16+
const result = await Bridge.call("dbt.lineage", {
17+
manifest_path: args.manifest_path,
18+
model: args.model,
19+
dialect: args.dialect,
20+
})
21+
22+
return {
23+
title: `dbt Lineage: ${result.model_name}${result.edges.length} edge(s) [${result.confidence}]`,
24+
metadata: {
25+
model_name: result.model_name,
26+
edgeCount: result.edges.length,
27+
tableCount: result.tables.length,
28+
confidence: result.confidence,
29+
},
30+
output: formatDbtLineage(result),
31+
}
32+
} catch (e) {
33+
const msg = e instanceof Error ? e.message : String(e)
34+
return {
35+
title: "dbt Lineage: ERROR",
36+
metadata: { model_name: args.model, edgeCount: 0, tableCount: 0, confidence: "unknown" },
37+
output: `Failed: ${msg}`,
38+
}
39+
}
40+
},
41+
})
42+
43+
function formatDbtLineage(result: DbtLineageResult): string {
44+
const lines: string[] = []
45+
46+
lines.push(`Model: ${result.model_name}`)
47+
if (result.model_unique_id) lines.push(`ID: ${result.model_unique_id}`)
48+
lines.push("")
49+
50+
if (result.confidence_factors.length > 0) {
51+
lines.push(`Confidence: ${result.confidence}`)
52+
lines.push(` Note: ${result.confidence_factors.join("; ")}`)
53+
lines.push("")
54+
}
55+
56+
if (result.edges.length === 0) {
57+
lines.push("No column-level lineage edges detected.")
58+
if (!result.compiled_sql) {
59+
lines.push("Run `dbt compile` first to generate compiled SQL.")
60+
}
61+
return lines.join("\n")
62+
}
63+
64+
lines.push("Column Lineage:")
65+
lines.push("Source → Target | Transform")
66+
lines.push("".padEnd(60, "-"))
67+
68+
for (const edge of result.edges) {
69+
const transform = edge.transform ? ` | ${edge.transform}` : ""
70+
lines.push(`${edge.source_table}.${edge.source_column}${edge.target_table}.${edge.target_column}${transform}`)
71+
}
72+
73+
lines.push("")
74+
lines.push(`Tables: ${result.tables.join(", ")}`)
75+
76+
return lines.join("\n")
77+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import z from "zod"
2+
import { Tool } from "./tool"
3+
import { Bridge } from "../bridge/client"
4+
5+
export const DbtProfilesTool = Tool.define("dbt_profiles", {
6+
description:
7+
"Discover dbt profiles from profiles.yml and map them to warehouse connections. Auto-detects Snowflake, BigQuery, Databricks, Postgres, Redshift, MySQL, DuckDB configurations.",
8+
parameters: z.object({
9+
path: z.string().optional().describe("Path to profiles.yml (defaults to ~/.dbt/profiles.yml)"),
10+
}),
11+
async execute(args, ctx) {
12+
try {
13+
const result = await Bridge.call("dbt.profiles", {
14+
path: args.path,
15+
})
16+
17+
if (!result.success) {
18+
return {
19+
title: "dbt Profiles: FAILED",
20+
metadata: { success: false, connection_count: 0 },
21+
output: result.error ?? "Failed to parse profiles.yml",
22+
}
23+
}
24+
25+
const connections = result.connections ?? []
26+
if (connections.length === 0) {
27+
return {
28+
title: "dbt Profiles: No connections found",
29+
metadata: { success: true, connection_count: 0 },
30+
output: "No dbt profiles found. Ensure ~/.dbt/profiles.yml exists with valid configurations.",
31+
}
32+
}
33+
34+
return {
35+
title: `dbt Profiles: ${connections.length} connection(s)`,
36+
metadata: { success: true, connection_count: connections.length },
37+
output: formatConnections(connections),
38+
}
39+
} catch (e) {
40+
const msg = e instanceof Error ? e.message : String(e)
41+
return {
42+
title: "dbt Profiles: ERROR",
43+
metadata: { success: false, connection_count: 0 },
44+
output: `Failed: ${msg}`,
45+
}
46+
}
47+
},
48+
})
49+
50+
function formatConnections(connections: Array<{ name: string; type: string; config: Record<string, unknown> }>): string {
51+
const lines: string[] = []
52+
for (const conn of connections) {
53+
lines.push(`${conn.name} (${conn.type})`)
54+
for (const [key, val] of Object.entries(conn.config)) {
55+
if (key === "password" || key === "private_key_passphrase" || key === "access_token") {
56+
lines.push(` ${key}: ****`)
57+
} else {
58+
lines.push(` ${key}: ${val}`)
59+
}
60+
}
61+
lines.push("")
62+
}
63+
return lines.join("\n")
64+
}

packages/altimate-code/src/tool/lineage-check.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,17 @@ export const LineageCheckTool = Tool.define("lineage_check", {
1313
.optional()
1414
.default("snowflake")
1515
.describe("SQL dialect (snowflake, postgres, bigquery, duckdb, etc.)"),
16+
schema_context: z
17+
.record(z.string(), z.array(z.object({ name: z.string(), data_type: z.string() })))
18+
.optional()
19+
.describe("Schema context mapping table names to column definitions for accurate lineage"),
1620
}),
1721
async execute(args, ctx) {
1822
try {
1923
const result = await Bridge.call("lineage.check", {
2024
sql: args.sql,
2125
dialect: args.dialect,
26+
schema_context: args.schema_context,
2227
})
2328

2429
return {

packages/altimate-code/src/tool/registry.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ import { SqlRecordFeedbackTool } from "./sql-record-feedback"
3636
import { SqlPredictCostTool } from "./sql-predict-cost"
3737
import { DbtRunTool } from "./dbt-run"
3838
import { DbtManifestTool } from "./dbt-manifest"
39+
import { DbtProfilesTool } from "./dbt-profiles"
40+
import { DbtLineageTool } from "./dbt-lineage"
3941
import { SchemaIndexTool } from "./schema-index"
4042
import { SchemaSearchTool } from "./schema-search"
4143
import { SchemaCacheStatusTool } from "./schema-cache-status"
@@ -192,6 +194,8 @@ export namespace ToolRegistry {
192194
SqlPredictCostTool,
193195
DbtRunTool,
194196
DbtManifestTool,
197+
DbtProfilesTool,
198+
DbtLineageTool,
195199
SchemaIndexTool,
196200
SchemaSearchTool,
197201
SchemaCacheStatusTool,

0 commit comments

Comments
 (0)