lf-cli 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  require 'thor'
2
+ require 'json'
2
3
 
3
4
  module Langfuse
4
5
  module CLI
@@ -31,13 +32,13 @@ module Langfuse
31
32
  EXAMPLES:
32
33
 
33
34
  # List all generations
34
- langfuse observations list --type generation
35
+ lf observations list --type generation
35
36
 
36
37
  # List observations for a specific trace
37
- langfuse observations list --trace-id trace_123
38
+ lf observations list --trace-id trace_123
38
39
 
39
40
  # List recent observations
40
- langfuse observations list --from "1 hour ago" --limit 20
41
+ lf observations list --from "1 hour ago" --limit 20
41
42
 
42
43
  API REFERENCE:
43
44
  Full API documentation: https://api.reference.langfuse.com/
@@ -57,23 +58,19 @@ module Langfuse
57
58
  observations = client.list_observations(filters)
58
59
  output_result(observations)
59
60
  rescue Client::AuthenticationError => e
60
- puts "Authentication Error: #{e.message}"
61
- exit 1
61
+ raise_cli_error("Authentication Error: #{e.message}")
62
62
  rescue Client::APIError => e
63
- puts "Error: #{e.message}"
64
- exit 1
63
+ raise_cli_error("Error: #{e.message}")
65
64
  end
66
65
 
67
66
  desc 'get OBSERVATION_ID', 'Get a specific observation'
68
67
  def get(observation_id)
69
68
  observation = client.get_observation(observation_id)
70
69
  output_result(observation)
71
- rescue Client::NotFoundError => e
72
- puts "Error: Observation not found - #{observation_id}"
73
- exit 1
70
+ rescue Client::NotFoundError
71
+ raise_cli_error("Observation not found - #{observation_id}")
74
72
  rescue Client::APIError => e
75
- puts "Error: #{e.message}"
76
- exit 1
73
+ raise_cli_error("Error: #{e.message}")
77
74
  end
78
75
 
79
76
  private
@@ -83,7 +80,7 @@ module Langfuse
83
80
  config = load_config
84
81
  unless config.valid?
85
82
  error_message = "Missing required configuration: #{config.missing_fields.join(', ')}"
86
- error_message += "\n\nPlease set environment variables or run: langfuse config setup"
83
+ error_message += "\n\nPlease set environment variables or run: lf config setup"
87
84
  raise Error, error_message
88
85
  end
89
86
  Client.new(config)
@@ -115,35 +112,37 @@ module Langfuse
115
112
  end
116
113
 
117
114
  def output_result(data)
118
- formatted = format_output(data)
115
+ format_type = parent_options[:format] || 'json'
119
116
 
120
117
  if parent_options[:output]
121
- File.write(parent_options[:output], formatted)
118
+ write_output(parent_options[:output], data, format_type)
122
119
  puts "Output written to #{parent_options[:output]}" if parent_options[:verbose]
123
120
  else
124
- puts formatted
121
+ puts format_output(data, format_type: format_type)
125
122
  end
126
123
  end
127
124
 
128
- def format_output(data)
129
- format_type = parent_options[:format] || 'table'
130
-
125
+ def format_output(data, format_type: 'json')
131
126
  case format_type
132
127
  when 'json'
133
- require 'json'
134
128
  JSON.pretty_generate(data)
135
129
  when 'csv'
136
130
  require_relative '../formatters/csv_formatter'
137
131
  Formatters::CSVFormatter.format(data)
138
- when 'markdown'
139
- require_relative '../formatters/markdown_formatter'
140
- Formatters::MarkdownFormatter.format(data)
141
132
  else # table
142
133
  require_relative '../formatters/table_formatter'
143
134
  Formatters::TableFormatter.format(data)
144
135
  end
145
136
  end
146
137
 
138
+ def write_output(path, data, format_type)
139
+ if format_type == 'json'
140
+ File.open(path, 'w') { |file| JSON.dump(data, file) }
141
+ else
142
+ File.write(path, format_output(data, format_type: format_type))
143
+ end
144
+ end
145
+
147
146
  def parent_options
148
147
  @parent_options ||= begin
149
148
  if parent.respond_to?(:options)
@@ -155,6 +154,10 @@ module Langfuse
155
154
  {}
156
155
  end
157
156
  end
157
+
158
+ def raise_cli_error(message)
159
+ raise Langfuse::CLI::Error, message
160
+ end
158
161
  end
159
162
  end
160
163
  end
@@ -1,4 +1,5 @@
1
1
  require 'thor'
2
+ require 'json'
2
3
 
3
4
  module Langfuse
4
5
  module CLI
@@ -21,23 +22,19 @@ module Langfuse
21
22
  scores = client.list_scores(filters)
22
23
  output_result(scores)
23
24
  rescue Client::AuthenticationError => e
24
- puts "Authentication Error: #{e.message}"
25
- exit 1
25
+ raise_cli_error("Authentication Error: #{e.message}")
26
26
  rescue Client::APIError => e
27
- puts "Error: #{e.message}"
28
- exit 1
27
+ raise_cli_error("Error: #{e.message}")
29
28
  end
30
29
 
31
30
  desc 'get SCORE_ID', 'Get a specific score'
32
31
  def get(score_id)
33
32
  score = client.get_score(score_id)
34
33
  output_result(score)
35
- rescue Client::NotFoundError => e
36
- puts "Error: Score not found - #{score_id}"
37
- exit 1
34
+ rescue Client::NotFoundError
35
+ raise_cli_error("Score not found - #{score_id}")
38
36
  rescue Client::APIError => e
39
- puts "Error: #{e.message}"
40
- exit 1
37
+ raise_cli_error("Error: #{e.message}")
41
38
  end
42
39
 
43
40
  private
@@ -47,7 +44,7 @@ module Langfuse
47
44
  config = load_config
48
45
  unless config.valid?
49
46
  error_message = "Missing required configuration: #{config.missing_fields.join(', ')}"
50
- error_message += "\n\nPlease set environment variables or run: langfuse config setup"
47
+ error_message += "\n\nPlease set environment variables or run: lf config setup"
51
48
  raise Error, error_message
52
49
  end
53
50
  Client.new(config)
@@ -76,35 +73,37 @@ module Langfuse
76
73
  end
77
74
 
78
75
  def output_result(data)
79
- formatted = format_output(data)
76
+ format_type = parent_options[:format] || 'json'
80
77
 
81
78
  if parent_options[:output]
82
- File.write(parent_options[:output], formatted)
79
+ write_output(parent_options[:output], data, format_type)
83
80
  puts "Output written to #{parent_options[:output]}" if parent_options[:verbose]
84
81
  else
85
- puts formatted
82
+ puts format_output(data, format_type: format_type)
86
83
  end
87
84
  end
88
85
 
89
- def format_output(data)
90
- format_type = parent_options[:format] || 'table'
91
-
86
+ def format_output(data, format_type: 'json')
92
87
  case format_type
93
88
  when 'json'
94
- require 'json'
95
89
  JSON.pretty_generate(data)
96
90
  when 'csv'
97
91
  require_relative '../formatters/csv_formatter'
98
92
  Formatters::CSVFormatter.format(data)
99
- when 'markdown'
100
- require_relative '../formatters/markdown_formatter'
101
- Formatters::MarkdownFormatter.format(data)
102
93
  else # table
103
94
  require_relative '../formatters/table_formatter'
104
95
  Formatters::TableFormatter.format(data)
105
96
  end
106
97
  end
107
98
 
99
+ def write_output(path, data, format_type)
100
+ if format_type == 'json'
101
+ File.open(path, 'w') { |file| JSON.dump(data, file) }
102
+ else
103
+ File.write(path, format_output(data, format_type: format_type))
104
+ end
105
+ end
106
+
108
107
  def parent_options
109
108
  @parent_options ||= begin
110
109
  if parent.respond_to?(:options)
@@ -116,6 +115,10 @@ module Langfuse
116
115
  {}
117
116
  end
118
117
  end
118
+
119
+ def raise_cli_error(message)
120
+ raise Langfuse::CLI::Error, message
121
+ end
119
122
  end
120
123
  end
121
124
  end
@@ -1,4 +1,5 @@
1
1
  require 'thor'
2
+ require 'json'
2
3
 
3
4
  module Langfuse
4
5
  module CLI
@@ -20,11 +21,9 @@ module Langfuse
20
21
  sessions = client.list_sessions(filters)
21
22
  output_result(sessions)
22
23
  rescue Client::AuthenticationError => e
23
- puts "Authentication Error: #{e.message}"
24
- exit 1
24
+ raise_cli_error("Authentication Error: #{e.message}")
25
25
  rescue Client::APIError => e
26
- puts "Error: #{e.message}"
27
- exit 1
26
+ raise_cli_error("Error: #{e.message}")
28
27
  end
29
28
 
30
29
  desc 'show SESSION_ID', 'Show a specific session'
@@ -32,12 +31,10 @@ module Langfuse
32
31
  def show(session_id)
33
32
  session = client.get_session(session_id)
34
33
  output_result(session)
35
- rescue Client::NotFoundError => e
36
- puts "Error: Session not found - #{session_id}"
37
- exit 1
34
+ rescue Client::NotFoundError
35
+ raise_cli_error("Session not found - #{session_id}")
38
36
  rescue Client::APIError => e
39
- puts "Error: #{e.message}"
40
- exit 1
37
+ raise_cli_error("Error: #{e.message}")
41
38
  end
42
39
 
43
40
  private
@@ -47,7 +44,7 @@ module Langfuse
47
44
  config = load_config
48
45
  unless config.valid?
49
46
  error_message = "Missing required configuration: #{config.missing_fields.join(', ')}"
50
- error_message += "\n\nPlease set environment variables or run: langfuse config setup"
47
+ error_message += "\n\nPlease set environment variables or run: lf config setup"
51
48
  raise Error, error_message
52
49
  end
53
50
  Client.new(config)
@@ -75,35 +72,37 @@ module Langfuse
75
72
  end
76
73
 
77
74
  def output_result(data)
78
- formatted = format_output(data)
75
+ format_type = parent_options[:format] || 'json'
79
76
 
80
77
  if parent_options[:output]
81
- File.write(parent_options[:output], formatted)
78
+ write_output(parent_options[:output], data, format_type)
82
79
  puts "Output written to #{parent_options[:output]}" if parent_options[:verbose]
83
80
  else
84
- puts formatted
81
+ puts format_output(data, format_type: format_type)
85
82
  end
86
83
  end
87
84
 
88
- def format_output(data)
89
- format_type = parent_options[:format] || 'table'
90
-
85
+ def format_output(data, format_type: 'json')
91
86
  case format_type
92
87
  when 'json'
93
- require 'json'
94
88
  JSON.pretty_generate(data)
95
89
  when 'csv'
96
90
  require_relative '../formatters/csv_formatter'
97
91
  Formatters::CSVFormatter.format(data)
98
- when 'markdown'
99
- require_relative '../formatters/markdown_formatter'
100
- Formatters::MarkdownFormatter.format(data)
101
92
  else # table
102
93
  require_relative '../formatters/table_formatter'
103
94
  Formatters::TableFormatter.format(data)
104
95
  end
105
96
  end
106
97
 
98
+ def write_output(path, data, format_type)
99
+ if format_type == 'json'
100
+ File.open(path, 'w') { |file| JSON.dump(data, file) }
101
+ else
102
+ File.write(path, format_output(data, format_type: format_type))
103
+ end
104
+ end
105
+
107
106
  def parent_options
108
107
  @parent_options ||= begin
109
108
  if parent.respond_to?(:options)
@@ -115,6 +114,10 @@ module Langfuse
115
114
  {}
116
115
  end
117
116
  end
117
+
118
+ def raise_cli_error(message)
119
+ raise Langfuse::CLI::Error, message
120
+ end
118
121
  end
119
122
  end
120
123
  end
@@ -1,4 +1,5 @@
1
1
  require 'thor'
2
+ require 'json'
2
3
 
3
4
  module Langfuse
4
5
  module CLI
@@ -28,21 +29,21 @@ module Langfuse
28
29
  --from, --to: Time range (ISO 8601 or relative like "1 hour ago")
29
30
 
30
31
  OUTPUT OPTIONS:
31
- Global options: --format [table|json|csv|markdown], --output FILE
32
+ Global options: --format [json|table|csv], --output FILE
32
33
 
33
34
  EXAMPLES:
34
35
 
35
36
  # List recent traces
36
- langfuse traces list --from "1 hour ago" --limit 20
37
+ lf traces list --from "1 hour ago" --limit 20
37
38
 
38
39
  # Find traces by name
39
- langfuse traces list --name "chat_completion"
40
+ lf traces list --name "chat_completion"
40
41
 
41
42
  # Filter by user and session
42
- langfuse traces list --user-id user_123 --session-id sess_456
43
+ lf traces list --user-id user_123 --session-id sess_456
43
44
 
44
45
  # Export to CSV
45
- langfuse traces list --format csv --output traces.csv
46
+ lf traces list --format csv --output traces.csv
46
47
 
47
48
  API REFERENCE:
48
49
  Full API documentation: https://api.reference.langfuse.com/
@@ -60,24 +61,19 @@ module Langfuse
60
61
  traces = client.list_traces(filters)
61
62
  output_result(traces)
62
63
  rescue Client::AuthenticationError => e
63
- puts "Authentication Error: #{e.message}"
64
- exit 1
64
+ raise_cli_error("Authentication Error: #{e.message}")
65
65
  rescue Client::APIError => e
66
- puts "Error: #{e.message}"
67
- exit 1
66
+ raise_cli_error("Error: #{e.message}")
68
67
  end
69
68
 
70
- desc 'get TRACE_ID', 'Get a specific trace'
71
- option :with_observations, type: :boolean, default: false, desc: 'Include all observations'
69
+ desc 'get TRACE_ID', 'Get a specific trace with observations'
72
70
  def get(trace_id)
73
71
  trace = client.get_trace(trace_id)
74
- output_result(trace)
75
- rescue Client::NotFoundError => e
76
- puts "Error: Trace not found - #{trace_id}"
77
- exit 1
72
+ output_result(ensure_observations(trace))
73
+ rescue Client::NotFoundError => _e
74
+ raise_cli_error("Trace not found - #{trace_id}")
78
75
  rescue Client::APIError => e
79
- puts "Error: #{e.message}"
80
- exit 1
76
+ raise_cli_error("Error: #{e.message}")
81
77
  end
82
78
 
83
79
  private
@@ -87,7 +83,7 @@ module Langfuse
87
83
  config = load_config
88
84
  unless config.valid?
89
85
  error_message = "Missing required configuration: #{config.missing_fields.join(', ')}"
90
- error_message += "\n\nPlease set environment variables or run: langfuse config setup"
86
+ error_message += "\n\nPlease set environment variables or run: lf config setup"
91
87
  raise Error, error_message
92
88
  end
93
89
  Client.new(config)
@@ -119,35 +115,43 @@ module Langfuse
119
115
  end
120
116
 
121
117
  def output_result(data)
122
- formatted = format_output(data)
118
+ format_type = parent_options[:format] || 'json'
123
119
 
124
120
  if parent_options[:output]
125
- File.write(parent_options[:output], formatted)
121
+ write_output(parent_options[:output], data, format_type)
126
122
  puts "Output written to #{parent_options[:output]}" if parent_options[:verbose]
127
123
  else
128
- puts formatted
124
+ puts format_output(data, format_type: format_type)
129
125
  end
130
126
  end
131
127
 
132
- def format_output(data)
133
- format_type = parent_options[:format] || 'table'
134
-
128
+ def format_output(data, format_type: 'json')
135
129
  case format_type
136
130
  when 'json'
137
- require 'json'
138
131
  JSON.pretty_generate(data)
139
132
  when 'csv'
140
133
  require_relative '../formatters/csv_formatter'
141
134
  Formatters::CSVFormatter.format(data)
142
- when 'markdown'
143
- require_relative '../formatters/markdown_formatter'
144
- Formatters::MarkdownFormatter.format(data)
145
135
  else # table
146
136
  require_relative '../formatters/table_formatter'
147
137
  Formatters::TableFormatter.format(data)
148
138
  end
149
139
  end
150
140
 
141
+ def write_output(path, data, format_type)
142
+ if format_type == 'json'
143
+ File.open(path, 'w') { |file| JSON.dump(data, file) }
144
+ else
145
+ File.write(path, format_output(data, format_type: format_type))
146
+ end
147
+ end
148
+
149
+ def ensure_observations(trace)
150
+ return trace unless trace.is_a?(Hash)
151
+
152
+ trace.merge('observations' => trace['observations'] || [])
153
+ end
154
+
151
155
  def parent_options
152
156
  @parent_options ||= begin
153
157
  # Try to get parent options from Thor
@@ -160,6 +164,10 @@ module Langfuse
160
164
  {}
161
165
  end
162
166
  end
167
+
168
+ def raise_cli_error(message)
169
+ raise Langfuse::CLI::Error, message
170
+ end
163
171
  end
164
172
  end
165
173
  end
@@ -1,25 +1,30 @@
1
1
  require 'yaml'
2
2
  require 'fileutils'
3
+ require 'sorbet-runtime'
3
4
 
4
5
  module Langfuse
5
6
  module CLI
6
7
  class Config
8
+ extend T::Sig
9
+
7
10
  attr_accessor :public_key, :secret_key, :host, :profile, :output_format, :page_limit
8
11
 
9
12
  DEFAULT_HOST = 'https://cloud.langfuse.com'
10
- DEFAULT_OUTPUT_FORMAT = 'table'
13
+ DEFAULT_OUTPUT_FORMAT = 'json'
11
14
  DEFAULT_PAGE_LIMIT = 50
12
15
  CONFIG_DIR = File.expand_path('~/.langfuse')
13
16
  CONFIG_FILE = File.join(CONFIG_DIR, 'config.yml')
14
17
 
18
+ sig { params(options: T::Hash[Symbol, T.untyped]).void }
15
19
  def initialize(options = {})
16
- @profile = options[:profile] || ENV['LANGFUSE_PROFILE'] || 'default'
20
+ @profile = normalize_string(options[:profile]) || normalize_string(ENV['LANGFUSE_PROFILE']) || 'default'
17
21
  load_config
18
22
  merge_options(options)
19
23
  end
20
24
 
21
25
  # Load configuration from file and environment variables
22
26
  # Priority: passed options > ENV vars > config file > defaults
27
+ sig { void }
23
28
  def load_config
24
29
  # Start with defaults
25
30
  @host = DEFAULT_HOST
@@ -38,52 +43,60 @@ module Langfuse
38
43
  end
39
44
 
40
45
  # Load configuration from YAML file
46
+ sig { void }
41
47
  def load_from_file
42
- config_data = YAML.load_file(CONFIG_FILE)
48
+ config_data = read_config_data
43
49
 
44
50
  # Load profile-specific config
45
- profile_config = config_data.dig('profiles', @profile) || config_data['default'] || {}
46
-
47
- @public_key = profile_config['public_key'] if profile_config['public_key']
48
- @secret_key = profile_config['secret_key'] if profile_config['secret_key']
49
- @host = profile_config['host'] if profile_config['host']
50
- @output_format = profile_config['output_format'] if profile_config['output_format']
51
- @page_limit = profile_config['page_limit'] if profile_config['page_limit']
51
+ profiles = config_data['profiles'].is_a?(Hash) ? config_data['profiles'] : {}
52
+ legacy_default = config_data['default'].is_a?(Hash) ? config_data['default'] : {}
53
+ profile_config = profiles[@profile] || profiles['default'] || legacy_default
54
+
55
+ @public_key = normalize_string(profile_config['public_key']) || @public_key
56
+ @secret_key = normalize_string(profile_config['secret_key']) || @secret_key
57
+ @host = normalize_string(profile_config['host']) || @host
58
+ @output_format = normalize_string(profile_config['output_format']) || @output_format
59
+ @page_limit = normalize_integer(profile_config['page_limit']) || @page_limit
52
60
  rescue => e
53
61
  warn "Warning: Error loading config file: #{e.message}"
54
62
  end
55
63
 
56
64
  # Load configuration from environment variables
65
+ sig { void }
57
66
  def load_from_env
58
- @public_key = ENV['LANGFUSE_PUBLIC_KEY'] if ENV['LANGFUSE_PUBLIC_KEY']
59
- @secret_key = ENV['LANGFUSE_SECRET_KEY'] if ENV['LANGFUSE_SECRET_KEY']
60
- @host = ENV['LANGFUSE_HOST'] if ENV['LANGFUSE_HOST']
67
+ @public_key = normalize_string(ENV['LANGFUSE_PUBLIC_KEY']) || @public_key
68
+ @secret_key = normalize_string(ENV['LANGFUSE_SECRET_KEY']) || @secret_key
69
+ @host = normalize_string(ENV['LANGFUSE_HOST']) || @host
61
70
  end
62
71
 
63
72
  # Merge passed options (highest priority)
73
+ sig { params(options: T::Hash[Symbol, T.untyped]).void }
64
74
  def merge_options(options)
65
- @public_key = options[:public_key] if options[:public_key]
66
- @secret_key = options[:secret_key] if options[:secret_key]
67
- @host = options[:host] if options[:host]
68
- @output_format = options[:format] if options[:format]
69
- @page_limit = options[:limit] if options[:limit]
75
+ @public_key = normalize_string(options[:public_key]) || @public_key
76
+ @secret_key = normalize_string(options[:secret_key]) || @secret_key
77
+ @host = normalize_string(options[:host]) || @host
78
+ @output_format = normalize_string(options[:format]) || @output_format
79
+ @page_limit = normalize_integer(options[:limit]) || @page_limit
70
80
  end
71
81
 
72
82
  # Validate that required configuration is present
83
+ sig { returns(T::Boolean) }
73
84
  def valid?
74
- !@public_key.nil? && !@secret_key.nil? && !@host.nil?
85
+ present?(@public_key) && present?(@secret_key) && present?(@host)
75
86
  end
76
87
 
77
88
  # Get list of missing required fields
89
+ sig { returns(T::Array[String]) }
78
90
  def missing_fields
79
91
  fields = []
80
- fields << 'public_key' if @public_key.nil?
81
- fields << 'secret_key' if @secret_key.nil?
82
- fields << 'host' if @host.nil?
92
+ fields << 'public_key' unless present?(@public_key)
93
+ fields << 'secret_key' unless present?(@secret_key)
94
+ fields << 'host' unless present?(@host)
83
95
  fields
84
96
  end
85
97
 
86
98
  # Save current configuration to file
99
+ sig { params(profile_name: T.nilable(String)).returns(T::Boolean) }
87
100
  def save(profile_name = nil)
88
101
  profile_name ||= @profile
89
102
 
@@ -91,7 +104,7 @@ module Langfuse
91
104
  FileUtils.mkdir_p(CONFIG_DIR)
92
105
 
93
106
  # Load existing config or create new
94
- config_data = File.exist?(CONFIG_FILE) ? YAML.load_file(CONFIG_FILE) : {}
107
+ config_data = File.exist?(CONFIG_FILE) ? read_config_data : {}
95
108
  config_data['profiles'] ||= {}
96
109
 
97
110
  # Update profile
@@ -116,11 +129,13 @@ module Langfuse
116
129
  end
117
130
 
118
131
  # Load a specific profile
132
+ sig { params(profile: T.nilable(String)).returns(Config) }
119
133
  def self.load(profile = nil)
120
134
  new(profile: profile)
121
135
  end
122
136
 
123
137
  # Get configuration as a hash
138
+ sig { returns(T::Hash[Symbol, T.untyped]) }
124
139
  def to_h
125
140
  {
126
141
  public_key: @public_key,
@@ -131,6 +146,43 @@ module Langfuse
131
146
  page_limit: @page_limit
132
147
  }
133
148
  end
149
+
150
+ private
151
+
152
+ sig { params(value: T.untyped).returns(T.nilable(String)) }
153
+ def normalize_string(value)
154
+ return nil if value.nil?
155
+
156
+ normalized = value.to_s.strip
157
+ normalized.empty? ? nil : normalized
158
+ end
159
+
160
+ sig { params(value: T.untyped).returns(T.nilable(Integer)) }
161
+ def normalize_integer(value)
162
+ return nil if value.nil?
163
+
164
+ Integer(value)
165
+ rescue ArgumentError, TypeError
166
+ nil
167
+ end
168
+
169
+ sig { params(value: T.nilable(String)).returns(T::Boolean) }
170
+ def present?(value)
171
+ !value.nil? && !value.strip.empty?
172
+ end
173
+
174
+ sig { returns(T::Hash[String, T.untyped]) }
175
+ def read_config_data
176
+ data = YAML.safe_load(
177
+ File.read(CONFIG_FILE),
178
+ permitted_classes: [],
179
+ permitted_symbols: [],
180
+ aliases: false
181
+ )
182
+ data.is_a?(Hash) ? data : {}
183
+ rescue Errno::ENOENT
184
+ {}
185
+ end
134
186
  end
135
187
  end
136
188
  end
@@ -1,5 +1,6 @@
1
1
  require 'csv'
2
2
  require 'json'
3
+ require 'date'
3
4
 
4
5
  module Langfuse
5
6
  module CLI