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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +38 -9
- data/README.md +197 -19
- data/lib/langfuse/cli/client.rb +20 -3
- data/lib/langfuse/cli/commands/config.rb +20 -10
- data/lib/langfuse/cli/commands/metrics.rb +23 -18
- data/lib/langfuse/cli/commands/observations.rb +26 -23
- data/lib/langfuse/cli/commands/scores.rb +23 -20
- data/lib/langfuse/cli/commands/sessions.rb +23 -20
- data/lib/langfuse/cli/commands/traces.rb +36 -28
- data/lib/langfuse/cli/config.rb +75 -23
- data/lib/langfuse/cli/formatters/csv_formatter.rb +1 -0
- data/lib/langfuse/cli/formatters/table_formatter.rb +22 -6
- data/lib/langfuse/cli/types.rb +0 -1
- data/lib/langfuse/cli/version.rb +1 -1
- data/lib/langfuse/cli.rb +17 -12
- metadata +3 -4
- data/lib/langfuse/cli/formatters/markdown_formatter.rb +0 -56
|
@@ -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
|
-
|
|
35
|
+
lf observations list --type generation
|
|
35
36
|
|
|
36
37
|
# List observations for a specific trace
|
|
37
|
-
|
|
38
|
+
lf observations list --trace-id trace_123
|
|
38
39
|
|
|
39
40
|
# List recent observations
|
|
40
|
-
|
|
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
|
-
|
|
61
|
-
exit 1
|
|
61
|
+
raise_cli_error("Authentication Error: #{e.message}")
|
|
62
62
|
rescue Client::APIError => e
|
|
63
|
-
|
|
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
|
|
72
|
-
|
|
73
|
-
exit 1
|
|
70
|
+
rescue Client::NotFoundError
|
|
71
|
+
raise_cli_error("Observation not found - #{observation_id}")
|
|
74
72
|
rescue Client::APIError => e
|
|
75
|
-
|
|
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:
|
|
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
|
-
|
|
115
|
+
format_type = parent_options[:format] || 'json'
|
|
119
116
|
|
|
120
117
|
if parent_options[:output]
|
|
121
|
-
|
|
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
|
|
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
|
-
|
|
25
|
-
exit 1
|
|
25
|
+
raise_cli_error("Authentication Error: #{e.message}")
|
|
26
26
|
rescue Client::APIError => e
|
|
27
|
-
|
|
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
|
|
36
|
-
|
|
37
|
-
exit 1
|
|
34
|
+
rescue Client::NotFoundError
|
|
35
|
+
raise_cli_error("Score not found - #{score_id}")
|
|
38
36
|
rescue Client::APIError => e
|
|
39
|
-
|
|
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:
|
|
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
|
-
|
|
76
|
+
format_type = parent_options[:format] || 'json'
|
|
80
77
|
|
|
81
78
|
if parent_options[:output]
|
|
82
|
-
|
|
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
|
|
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
|
-
|
|
24
|
-
exit 1
|
|
24
|
+
raise_cli_error("Authentication Error: #{e.message}")
|
|
25
25
|
rescue Client::APIError => e
|
|
26
|
-
|
|
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
|
|
36
|
-
|
|
37
|
-
exit 1
|
|
34
|
+
rescue Client::NotFoundError
|
|
35
|
+
raise_cli_error("Session not found - #{session_id}")
|
|
38
36
|
rescue Client::APIError => e
|
|
39
|
-
|
|
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:
|
|
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
|
-
|
|
75
|
+
format_type = parent_options[:format] || 'json'
|
|
79
76
|
|
|
80
77
|
if parent_options[:output]
|
|
81
|
-
|
|
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
|
|
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|
|
|
32
|
+
Global options: --format [json|table|csv], --output FILE
|
|
32
33
|
|
|
33
34
|
EXAMPLES:
|
|
34
35
|
|
|
35
36
|
# List recent traces
|
|
36
|
-
|
|
37
|
+
lf traces list --from "1 hour ago" --limit 20
|
|
37
38
|
|
|
38
39
|
# Find traces by name
|
|
39
|
-
|
|
40
|
+
lf traces list --name "chat_completion"
|
|
40
41
|
|
|
41
42
|
# Filter by user and session
|
|
42
|
-
|
|
43
|
+
lf traces list --user-id user_123 --session-id sess_456
|
|
43
44
|
|
|
44
45
|
# Export to CSV
|
|
45
|
-
|
|
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
|
-
|
|
64
|
-
exit 1
|
|
64
|
+
raise_cli_error("Authentication Error: #{e.message}")
|
|
65
65
|
rescue Client::APIError => e
|
|
66
|
-
|
|
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 =>
|
|
76
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
118
|
+
format_type = parent_options[:format] || 'json'
|
|
123
119
|
|
|
124
120
|
if parent_options[:output]
|
|
125
|
-
|
|
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
|
|
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
|
data/lib/langfuse/cli/config.rb
CHANGED
|
@@ -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 = '
|
|
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 =
|
|
48
|
+
config_data = read_config_data
|
|
43
49
|
|
|
44
50
|
# Load profile-specific config
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
@
|
|
50
|
-
@
|
|
51
|
-
@
|
|
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']
|
|
59
|
-
@secret_key = ENV['LANGFUSE_SECRET_KEY']
|
|
60
|
-
@host = 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]
|
|
66
|
-
@secret_key = options[:secret_key]
|
|
67
|
-
@host = options[:host]
|
|
68
|
-
@output_format = options[:format]
|
|
69
|
-
@page_limit = 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
|
-
|
|
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'
|
|
81
|
-
fields << 'secret_key'
|
|
82
|
-
fields << 'host'
|
|
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) ?
|
|
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
|