Class: Strata::CLI::AI::Services::TableGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/strata/cli/ai/services/table_generator.rb

Overview

Generates semantic model fields from database table metadata using AI. Returns structured JSON for use in the field editor.

Constant Summary collapse

SYSTEM_PROMPT =
<<~PROMPT
  You are a semantic modeling expert for the Strata Business Intelligence platform.
  Analyze database columns and generate semantic field definitions.

  You must understand that Names are extremely important in the Strata application. When the same named dimension
  is mapped to multiple tables, Strata treats them as the same entity but with multiple potential tables. Strata
  will at query generation time choose the best table. Therefore it is extremely important to consider whether a
  potential dimension or measure is the same as an existing one. You can infer that based on the table name,
  column name, existing fields, and business context. Reuse an existing dimension name only when it represents
  the same business concept across tables. If the same column label describes the current table's own entity,
  lifecycle, or attributes, create a table-specific name instead of reusing a generic existing name.
  Example:
    existing dimension name: Record Start Date
    table name: web_page
    column name: record_start_date
    dimension name: Web Page Record Start Date
  Example:
    existing dimension name: Country
    table name: customer
    column name: country
    dimension name: Country
  The edge case is when you have a Dimension table like Customer with column first_name and another Dimension
  table called Billed Customer with first_name, then they are likely not the same. A dimension created from
  Billed Customer should be given an appropriately prefixed name: Billed Customer First Name.

  Next, if the column is something highly ambiguous and the current table is likely a dimension table, prefix
  the dimension name with an appropriate prefix based on the name of the dimension table.
  Example:
    table name: item_dim
    column name: color
    dimension name: Item Color
  Example:
    table name: customer
    column name: first name
    dimension name: Customer First Name

  Given the above for each column, determine:
  - name: Human-friendly field name (e.g., "customer_id" → "Customer ID")
  - description: Brief description of what this field represents
  - schema_type: "dimension" for categorical/text, "measure" for numeric aggregations
  - data_type: string, integer, bigint, decimal, date, date_time, boolean. These are the data types supported
    by strata. They do not have precision nor scale. Simply the types listed here. Do not apply any other data types.
  - expression: SQL expression (for measures include aggregation like "sum(amount)")
  - synonyms: Array of 0-3 alternative names users might use to refer to this field.
    These help AI search and natural language queries find the right field.
    Example: "Revenue" → ["sales"]
    Example: "Created At" → ["created date", "date"]
    Example: "State" in a geography table → ["province"]
    Example: "Customer Return Date" → [] -- name has high specificity already so no synonyms needed 

  In cases where you intentionally reuse an existing dimension name because it is the same business concept,
  omit everything except the following fields: name, schema_type, data_type, expression.

  Output ONLY valid JSON array, no explanations.
PROMPT

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(client: Client.new) ⇒ TableGenerator

Returns a new instance of TableGenerator.



72
73
74
# File 'lib/strata/cli/ai/services/table_generator.rb', line 72

def initialize(client: Client.new)
  @client = client
end

Instance Attribute Details

#clientObject (readonly)

Returns the value of attribute client.



70
71
72
# File 'lib/strata/cli/ai/services/table_generator.rb', line 70

def client
  @client
end

Instance Method Details

#ai_available?Boolean

Check if AI is available

Returns:

  • (Boolean)


134
135
136
# File 'lib/strata/cli/ai/services/table_generator.rb', line 134

def ai_available?
  @client.enabled?
end

#call(table_name:, columns:, datasource:, user_context: nil) ⇒ Hash?

Generate field definitions from table metadata

Parameters:

  • table_name (String)

    Full table name (schema.table)

  • columns (Array<Hash>)

    Column metadata with :name, :type

  • datasource (String)

    Datasource key

  • user_context (Hash, nil) (defaults to: nil)

    Optional user-provided context with :description

Returns:

  • (Hash, nil)

    Generated model data or nil if AI disabled



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/strata/cli/ai/services/table_generator.rb', line 82

def call(table_name:, columns:, datasource:, user_context: nil)
  unless ai_available?
    Output.print_warning("AI not available - using rule-based fallback")
    return fallback_fields(columns)
  end

  existing_models = load_existing_models_context
  prompt = build_prompt(
    table_name: table_name,
    columns: columns,
    existing_models_context: existing_models,
    user_context: user_context
  )

  begin
    response = @client.complete(prompt, system_prompt: SYSTEM_PROMPT)
    parse_response(response, columns)
  rescue => e
    # Fallback to basic field generation on AI error
    Output.print_warning("AI failed to generate fields: #{e.message} - performing basic field generation")
    fallback_fields(columns)
  end
end

#call_with_prompt(table_name:, columns:, datasource:, user_prompt:, current_fields:) ⇒ Array<Hash>

Generate fields with user prompt for modification/regeneration

Parameters:

  • table_name (String)

    Full table name

  • columns (Array<Hash>)

    Column metadata

  • datasource (String)

    Datasource key

  • user_prompt (String)

    User’s natural language prompt

  • current_fields (Array<Hash>)

    Current field definitions

Returns:

  • (Array<Hash>)

    Regenerated fields



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/strata/cli/ai/services/table_generator.rb', line 113

def call_with_prompt(table_name:, columns:, datasource:, user_prompt:, current_fields:)
  unless ai_available?
    raise AIError, "AI is not available - configure ai_api_key in .strata"
  end

  prompt = build_prompt_with_user_input(
    table_name: table_name,
    columns: columns,
    user_prompt: user_prompt,
    current_fields: current_fields
  )

  begin
    response = @client.complete(prompt, system_prompt: SYSTEM_PROMPT)
    parse_response(response, columns)
  rescue => e
    raise AIError, "AI failed to generate fields: #{e.message}"
  end
end