lf-cli 1.0.1 → 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 +10 -0
- data/README.md +13 -23
- data/lib/langfuse/cli/commands/metrics.rb +2 -5
- data/lib/langfuse/cli/commands/observations.rb +2 -5
- data/lib/langfuse/cli/commands/scores.rb +2 -5
- data/lib/langfuse/cli/commands/sessions.rb +2 -5
- data/lib/langfuse/cli/commands/traces.rb +11 -9
- data/lib/langfuse/cli/config.rb +1 -1
- data/lib/langfuse/cli/types.rb +0 -1
- data/lib/langfuse/cli/version.rb +1 -1
- data/lib/langfuse/cli.rb +4 -7
- metadata +1 -2
- data/lib/langfuse/cli/formatters/markdown_formatter.rb +0 -78
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 06ff677435b9202de2481d4aa72805639508431a90805be2d0adbb50e54bbcff
|
|
4
|
+
data.tar.gz: 425bcfc9371f4c6c497f50b2082f0d54e7d0a85b7fcd737988ff60d7f9edfac2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 64c05599e12dc8ee8f9a414519d14f5912dddfa969a0cfaaaf7c5f81bb689a7c7587de7886716739bfb2e82d04063447655f840b9c9c9577d6d4fa7875c98600
|
|
7
|
+
data.tar.gz: 345323d04554a45007cff5936ff300dd8df0d29dcca874fd155122a94150b2b4c1f8f224d279c7c405924edefd838b59faf343b9c6f9cf5ca60615ef8a356a32
|
data/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
14
14
|
- Creating/updating scores via CLI
|
|
15
15
|
- Integration with other observability tools
|
|
16
16
|
|
|
17
|
+
## [1.0.2] - 2026-02-11
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- `json` is now the default output format for all commands.
|
|
21
|
+
- Removed `markdown` as a supported output format.
|
|
22
|
+
- `lf traces get` now always returns an `observations` field in output.
|
|
23
|
+
|
|
24
|
+
### Removed
|
|
25
|
+
- Removed `--with-observations` / `--no-with-observations` from `lf traces get`.
|
|
26
|
+
|
|
17
27
|
## [1.0.1] - 2026-02-10
|
|
18
28
|
|
|
19
29
|
### Added
|
data/README.md
CHANGED
|
@@ -21,7 +21,7 @@ This project is not affiliated with, endorsed by, or sponsored by Langfuse GmbH.
|
|
|
21
21
|
- 🎯 **Inspect Observations** - List LLM generations, spans, and events
|
|
22
22
|
- 📈 **Query Metrics** - Run analytics queries with custom aggregations and dimensions
|
|
23
23
|
- ⭐ **View Scores** - Access quality scores and evaluation metrics
|
|
24
|
-
- 🎨 **Multiple Output Formats** -
|
|
24
|
+
- 🎨 **Multiple Output Formats** - JSON, Table, or CSV
|
|
25
25
|
- 🔐 **Multi-Profile Support** - Manage credentials for dev, staging, and production
|
|
26
26
|
- 🌐 **Interactive Setup** - Browser-integrated credential configuration
|
|
27
27
|
- ⚡ **Type-Safe** - Built with Sorbet Runtime for reliability
|
|
@@ -191,7 +191,7 @@ lf metrics query \
|
|
|
191
191
|
All commands support these global options:
|
|
192
192
|
|
|
193
193
|
```bash
|
|
194
|
-
-f, --format [table|
|
|
194
|
+
-f, --format [json|table|csv] # Output format (default: json)
|
|
195
195
|
-o, --output FILE # Save output to file
|
|
196
196
|
-l, --limit N # Limit number of results
|
|
197
197
|
-P, --profile PROFILE # Use specific profile
|
|
@@ -216,21 +216,21 @@ Supports both ISO 8601 and natural language:
|
|
|
216
216
|
|
|
217
217
|
## Output Formats
|
|
218
218
|
|
|
219
|
-
###
|
|
219
|
+
### JSON (Default)
|
|
220
220
|
|
|
221
221
|
```bash
|
|
222
|
-
lf traces list --format
|
|
222
|
+
lf traces list --format json
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
-
|
|
225
|
+
Perfect for piping to `jq` or other tools.
|
|
226
226
|
|
|
227
|
-
###
|
|
227
|
+
### Table
|
|
228
228
|
|
|
229
229
|
```bash
|
|
230
|
-
lf traces list --format
|
|
230
|
+
lf traces list --format table
|
|
231
231
|
```
|
|
232
232
|
|
|
233
|
-
|
|
233
|
+
Outputs a formatted ASCII table - great for terminal viewing.
|
|
234
234
|
|
|
235
235
|
```bash
|
|
236
236
|
lf traces list --format json | jq '.[] | select(.name == "chat")'
|
|
@@ -244,14 +244,6 @@ lf traces list --format csv --output data.csv
|
|
|
244
244
|
|
|
245
245
|
Import into spreadsheets or data analysis tools.
|
|
246
246
|
|
|
247
|
-
### Markdown
|
|
248
|
-
|
|
249
|
-
```bash
|
|
250
|
-
lf traces list --format markdown
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
Great for documentation and reports.
|
|
254
|
-
|
|
255
247
|
## Configuration
|
|
256
248
|
|
|
257
249
|
### Configuration File
|
|
@@ -264,7 +256,7 @@ profiles:
|
|
|
264
256
|
host: https://cloud.langfuse.com
|
|
265
257
|
public_key: pk_...
|
|
266
258
|
secret_key: sk_...
|
|
267
|
-
output_format:
|
|
259
|
+
output_format: json
|
|
268
260
|
page_limit: 50
|
|
269
261
|
|
|
270
262
|
production:
|
|
@@ -337,7 +329,7 @@ This section mirrors the depth of a `llms-full.txt` so AI assistants can answer
|
|
|
337
329
|
| `-P, --profile PROFILE` | Selects a saved profile. | Defaults to `default`. |
|
|
338
330
|
| `--public-key KEY` / `--secret-key KEY` | Inject credentials without touching config files. | Highest priority source. |
|
|
339
331
|
| `--host URL` | Override Langfuse host. | Combine with `--profile` to temporarily test another region. |
|
|
340
|
-
| `-f, --format FORMAT` | `
|
|
332
|
+
| `-f, --format FORMAT` | `json` (default), `table`, `csv`. | Applies to every command. |
|
|
341
333
|
| `-o, --output PATH` | Write output to a file. | Respects format; prints “Output written…” when `--verbose`. |
|
|
342
334
|
| `-l, --limit N` | Caps number of records pulled per command. | Pagination helper, defaults to API `limit` (50) when omitted. |
|
|
343
335
|
| `-p, --page N` | Start from an explicit page. | Useful when you know an offset. |
|
|
@@ -350,9 +342,8 @@ Pagination strategy: the client keeps fetching pages until it collects the reque
|
|
|
350
342
|
### Output & Files
|
|
351
343
|
|
|
352
344
|
- `table` renders ASCII tables via `Formatters::TableFormatter` and truncates oversized cells for terminal safety.
|
|
353
|
-
- `json` prints pretty JSON to stdout for direct piping to `jq`.
|
|
354
|
-
- `csv`
|
|
355
|
-
- `markdown` escapes pipes, normalizes newlines to `<br>`, and truncates oversized cells.
|
|
345
|
+
- `json` is the default output and prints pretty JSON to stdout for direct piping to `jq`.
|
|
346
|
+
- `csv` uses a dedicated formatter and requires array-like data (single hashes are wrapped automatically).
|
|
356
347
|
- `--output` writes to a file; JSON output is written in compact form to reduce memory pressure on large payloads.
|
|
357
348
|
- Use `lf ... --format json | jq ...` for automation recipes.
|
|
358
349
|
|
|
@@ -374,7 +365,7 @@ Each command inherits the global flags above. API errors exit with status code `
|
|
|
374
365
|
| Subcommand | Purpose |
|
|
375
366
|
| --- | --- |
|
|
376
367
|
| `lf traces list` | Lists traces with filters/pagination. |
|
|
377
|
-
| `lf traces get TRACE_ID
|
|
368
|
+
| `lf traces get TRACE_ID` | Fetches a single trace with `observations` included. |
|
|
378
369
|
|
|
379
370
|
`traces list` options:
|
|
380
371
|
|
|
@@ -522,7 +513,6 @@ lib/langfuse/cli/
|
|
|
522
513
|
├── formatters/ # Output formatters
|
|
523
514
|
│ ├── table_formatter.rb
|
|
524
515
|
│ ├── csv_formatter.rb
|
|
525
|
-
│ └── markdown_formatter.rb
|
|
526
516
|
└── commands/ # Command modules
|
|
527
517
|
├── traces.rb
|
|
528
518
|
├── sessions.rb
|
|
@@ -146,7 +146,7 @@ module Langfuse
|
|
|
146
146
|
end
|
|
147
147
|
|
|
148
148
|
def output_result(data)
|
|
149
|
-
format_type = parent_options[:format] || '
|
|
149
|
+
format_type = parent_options[:format] || 'json'
|
|
150
150
|
|
|
151
151
|
if parent_options[:output]
|
|
152
152
|
write_output(parent_options[:output], data, format_type)
|
|
@@ -156,16 +156,13 @@ module Langfuse
|
|
|
156
156
|
end
|
|
157
157
|
end
|
|
158
158
|
|
|
159
|
-
def format_output(data, format_type: '
|
|
159
|
+
def format_output(data, format_type: 'json')
|
|
160
160
|
case format_type
|
|
161
161
|
when 'json'
|
|
162
162
|
JSON.pretty_generate(data)
|
|
163
163
|
when 'csv'
|
|
164
164
|
require_relative '../formatters/csv_formatter'
|
|
165
165
|
Formatters::CSVFormatter.format(data)
|
|
166
|
-
when 'markdown'
|
|
167
|
-
require_relative '../formatters/markdown_formatter'
|
|
168
|
-
Formatters::MarkdownFormatter.format(data)
|
|
169
166
|
else # table
|
|
170
167
|
require_relative '../formatters/table_formatter'
|
|
171
168
|
Formatters::TableFormatter.format(data)
|
|
@@ -112,7 +112,7 @@ module Langfuse
|
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
def output_result(data)
|
|
115
|
-
format_type = parent_options[:format] || '
|
|
115
|
+
format_type = parent_options[:format] || 'json'
|
|
116
116
|
|
|
117
117
|
if parent_options[:output]
|
|
118
118
|
write_output(parent_options[:output], data, format_type)
|
|
@@ -122,16 +122,13 @@ module Langfuse
|
|
|
122
122
|
end
|
|
123
123
|
end
|
|
124
124
|
|
|
125
|
-
def format_output(data, format_type: '
|
|
125
|
+
def format_output(data, format_type: 'json')
|
|
126
126
|
case format_type
|
|
127
127
|
when 'json'
|
|
128
128
|
JSON.pretty_generate(data)
|
|
129
129
|
when 'csv'
|
|
130
130
|
require_relative '../formatters/csv_formatter'
|
|
131
131
|
Formatters::CSVFormatter.format(data)
|
|
132
|
-
when 'markdown'
|
|
133
|
-
require_relative '../formatters/markdown_formatter'
|
|
134
|
-
Formatters::MarkdownFormatter.format(data)
|
|
135
132
|
else # table
|
|
136
133
|
require_relative '../formatters/table_formatter'
|
|
137
134
|
Formatters::TableFormatter.format(data)
|
|
@@ -73,7 +73,7 @@ module Langfuse
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def output_result(data)
|
|
76
|
-
format_type = parent_options[:format] || '
|
|
76
|
+
format_type = parent_options[:format] || 'json'
|
|
77
77
|
|
|
78
78
|
if parent_options[:output]
|
|
79
79
|
write_output(parent_options[:output], data, format_type)
|
|
@@ -83,16 +83,13 @@ module Langfuse
|
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
def format_output(data, format_type: '
|
|
86
|
+
def format_output(data, format_type: 'json')
|
|
87
87
|
case format_type
|
|
88
88
|
when 'json'
|
|
89
89
|
JSON.pretty_generate(data)
|
|
90
90
|
when 'csv'
|
|
91
91
|
require_relative '../formatters/csv_formatter'
|
|
92
92
|
Formatters::CSVFormatter.format(data)
|
|
93
|
-
when 'markdown'
|
|
94
|
-
require_relative '../formatters/markdown_formatter'
|
|
95
|
-
Formatters::MarkdownFormatter.format(data)
|
|
96
93
|
else # table
|
|
97
94
|
require_relative '../formatters/table_formatter'
|
|
98
95
|
Formatters::TableFormatter.format(data)
|
|
@@ -72,7 +72,7 @@ module Langfuse
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def output_result(data)
|
|
75
|
-
format_type = parent_options[:format] || '
|
|
75
|
+
format_type = parent_options[:format] || 'json'
|
|
76
76
|
|
|
77
77
|
if parent_options[:output]
|
|
78
78
|
write_output(parent_options[:output], data, format_type)
|
|
@@ -82,16 +82,13 @@ module Langfuse
|
|
|
82
82
|
end
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
-
def format_output(data, format_type: '
|
|
85
|
+
def format_output(data, format_type: 'json')
|
|
86
86
|
case format_type
|
|
87
87
|
when 'json'
|
|
88
88
|
JSON.pretty_generate(data)
|
|
89
89
|
when 'csv'
|
|
90
90
|
require_relative '../formatters/csv_formatter'
|
|
91
91
|
Formatters::CSVFormatter.format(data)
|
|
92
|
-
when 'markdown'
|
|
93
|
-
require_relative '../formatters/markdown_formatter'
|
|
94
|
-
Formatters::MarkdownFormatter.format(data)
|
|
95
92
|
else # table
|
|
96
93
|
require_relative '../formatters/table_formatter'
|
|
97
94
|
Formatters::TableFormatter.format(data)
|
|
@@ -29,7 +29,7 @@ module Langfuse
|
|
|
29
29
|
--from, --to: Time range (ISO 8601 or relative like "1 hour ago")
|
|
30
30
|
|
|
31
31
|
OUTPUT OPTIONS:
|
|
32
|
-
Global options: --format [table|
|
|
32
|
+
Global options: --format [json|table|csv], --output FILE
|
|
33
33
|
|
|
34
34
|
EXAMPLES:
|
|
35
35
|
|
|
@@ -66,11 +66,10 @@ module Langfuse
|
|
|
66
66
|
raise_cli_error("Error: #{e.message}")
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
-
desc 'get TRACE_ID', 'Get a specific trace'
|
|
70
|
-
option :with_observations, type: :boolean, default: false, desc: 'Include all observations'
|
|
69
|
+
desc 'get TRACE_ID', 'Get a specific trace with observations'
|
|
71
70
|
def get(trace_id)
|
|
72
71
|
trace = client.get_trace(trace_id)
|
|
73
|
-
output_result(trace)
|
|
72
|
+
output_result(ensure_observations(trace))
|
|
74
73
|
rescue Client::NotFoundError => _e
|
|
75
74
|
raise_cli_error("Trace not found - #{trace_id}")
|
|
76
75
|
rescue Client::APIError => e
|
|
@@ -116,7 +115,7 @@ module Langfuse
|
|
|
116
115
|
end
|
|
117
116
|
|
|
118
117
|
def output_result(data)
|
|
119
|
-
format_type = parent_options[:format] || '
|
|
118
|
+
format_type = parent_options[:format] || 'json'
|
|
120
119
|
|
|
121
120
|
if parent_options[:output]
|
|
122
121
|
write_output(parent_options[:output], data, format_type)
|
|
@@ -126,16 +125,13 @@ module Langfuse
|
|
|
126
125
|
end
|
|
127
126
|
end
|
|
128
127
|
|
|
129
|
-
def format_output(data, format_type: '
|
|
128
|
+
def format_output(data, format_type: 'json')
|
|
130
129
|
case format_type
|
|
131
130
|
when 'json'
|
|
132
131
|
JSON.pretty_generate(data)
|
|
133
132
|
when 'csv'
|
|
134
133
|
require_relative '../formatters/csv_formatter'
|
|
135
134
|
Formatters::CSVFormatter.format(data)
|
|
136
|
-
when 'markdown'
|
|
137
|
-
require_relative '../formatters/markdown_formatter'
|
|
138
|
-
Formatters::MarkdownFormatter.format(data)
|
|
139
135
|
else # table
|
|
140
136
|
require_relative '../formatters/table_formatter'
|
|
141
137
|
Formatters::TableFormatter.format(data)
|
|
@@ -150,6 +146,12 @@ module Langfuse
|
|
|
150
146
|
end
|
|
151
147
|
end
|
|
152
148
|
|
|
149
|
+
def ensure_observations(trace)
|
|
150
|
+
return trace unless trace.is_a?(Hash)
|
|
151
|
+
|
|
152
|
+
trace.merge('observations' => trace['observations'] || [])
|
|
153
|
+
end
|
|
154
|
+
|
|
153
155
|
def parent_options
|
|
154
156
|
@parent_options ||= begin
|
|
155
157
|
# Try to get parent options from Thor
|
data/lib/langfuse/cli/config.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Langfuse
|
|
|
10
10
|
attr_accessor :public_key, :secret_key, :host, :profile, :output_format, :page_limit
|
|
11
11
|
|
|
12
12
|
DEFAULT_HOST = 'https://cloud.langfuse.com'
|
|
13
|
-
DEFAULT_OUTPUT_FORMAT = '
|
|
13
|
+
DEFAULT_OUTPUT_FORMAT = 'json'
|
|
14
14
|
DEFAULT_PAGE_LIMIT = 50
|
|
15
15
|
CONFIG_DIR = File.expand_path('~/.langfuse')
|
|
16
16
|
CONFIG_FILE = File.join(CONFIG_DIR, 'config.yml')
|
data/lib/langfuse/cli/types.rb
CHANGED
data/lib/langfuse/cli/version.rb
CHANGED
data/lib/langfuse/cli.rb
CHANGED
|
@@ -28,8 +28,8 @@ module Langfuse
|
|
|
28
28
|
class_option :format,
|
|
29
29
|
type: :string,
|
|
30
30
|
aliases: '-f',
|
|
31
|
-
enum: %w[table
|
|
32
|
-
default: '
|
|
31
|
+
enum: %w[json table csv],
|
|
32
|
+
default: 'json',
|
|
33
33
|
desc: 'Output format'
|
|
34
34
|
class_option :output,
|
|
35
35
|
type: :string,
|
|
@@ -111,7 +111,7 @@ module Langfuse
|
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
def output_result(data)
|
|
114
|
-
format_type = options[:format] || '
|
|
114
|
+
format_type = options[:format] || 'json'
|
|
115
115
|
|
|
116
116
|
if options[:output]
|
|
117
117
|
write_output(options[:output], data, format_type)
|
|
@@ -121,7 +121,7 @@ module Langfuse
|
|
|
121
121
|
end
|
|
122
122
|
end
|
|
123
123
|
|
|
124
|
-
def format_output(data, format_type: '
|
|
124
|
+
def format_output(data, format_type: 'json')
|
|
125
125
|
case format_type
|
|
126
126
|
when 'json'
|
|
127
127
|
JSON.pretty_generate(data)
|
|
@@ -129,9 +129,6 @@ module Langfuse
|
|
|
129
129
|
# CSV formatting will be implemented in formatters
|
|
130
130
|
require_relative 'cli/formatters/csv_formatter'
|
|
131
131
|
Formatters::CSVFormatter.format(data)
|
|
132
|
-
when 'markdown'
|
|
133
|
-
require_relative 'cli/formatters/markdown_formatter'
|
|
134
|
-
Formatters::MarkdownFormatter.format(data)
|
|
135
132
|
else # table
|
|
136
133
|
require_relative 'cli/formatters/table_formatter'
|
|
137
134
|
Formatters::TableFormatter.format(data)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lf-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vicente Reig Rincon de Arellano
|
|
@@ -271,7 +271,6 @@ files:
|
|
|
271
271
|
- lib/langfuse/cli/commands/traces.rb
|
|
272
272
|
- lib/langfuse/cli/config.rb
|
|
273
273
|
- lib/langfuse/cli/formatters/csv_formatter.rb
|
|
274
|
-
- lib/langfuse/cli/formatters/markdown_formatter.rb
|
|
275
274
|
- lib/langfuse/cli/formatters/table_formatter.rb
|
|
276
275
|
- lib/langfuse/cli/types.rb
|
|
277
276
|
- lib/langfuse/cli/version.rb
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
require 'json'
|
|
2
|
-
require 'date'
|
|
3
|
-
require 'sorbet-runtime'
|
|
4
|
-
|
|
5
|
-
module Langfuse
|
|
6
|
-
module CLI
|
|
7
|
-
module Formatters
|
|
8
|
-
class MarkdownFormatter
|
|
9
|
-
extend T::Sig
|
|
10
|
-
|
|
11
|
-
DEFAULT_MAX_CELL_BYTES = 2048
|
|
12
|
-
|
|
13
|
-
sig { params(data: T.untyped, max_cell_bytes: Integer).returns(String) }
|
|
14
|
-
def self.format(data, max_cell_bytes: DEFAULT_MAX_CELL_BYTES)
|
|
15
|
-
return "No data to display" if data.nil? || (data.is_a?(Array) && data.empty?)
|
|
16
|
-
|
|
17
|
-
# Convert single hash to array for consistent handling
|
|
18
|
-
data = [data] unless data.is_a?(Array)
|
|
19
|
-
|
|
20
|
-
# Get all unique keys from all rows
|
|
21
|
-
headers = data.flat_map(&:keys).uniq
|
|
22
|
-
|
|
23
|
-
# Build markdown table
|
|
24
|
-
output = []
|
|
25
|
-
|
|
26
|
-
# Header row
|
|
27
|
-
output << "| #{headers.join(' | ')} |"
|
|
28
|
-
|
|
29
|
-
# Separator row
|
|
30
|
-
output << "| #{headers.map { '---' }.join(' | ')} |"
|
|
31
|
-
|
|
32
|
-
# Data rows
|
|
33
|
-
data.each do |row|
|
|
34
|
-
values = headers.map { |header| escape_pipes(format_value(row[header], max_cell_bytes: max_cell_bytes)) }
|
|
35
|
-
output << "| #{values.join(' | ')} |"
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
output.join("\n")
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
private
|
|
42
|
-
|
|
43
|
-
sig { params(value: T.untyped, max_cell_bytes: Integer).returns(String) }
|
|
44
|
-
def self.format_value(value, max_cell_bytes:)
|
|
45
|
-
case value
|
|
46
|
-
when nil
|
|
47
|
-
''
|
|
48
|
-
when Hash, Array
|
|
49
|
-
normalize(truncate(value.to_json, max_cell_bytes))
|
|
50
|
-
when Time, DateTime
|
|
51
|
-
normalize(truncate(value.iso8601, max_cell_bytes))
|
|
52
|
-
else
|
|
53
|
-
normalize(truncate(value.to_s, max_cell_bytes))
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
sig { params(str: String).returns(String) }
|
|
58
|
-
def self.escape_pipes(str)
|
|
59
|
-
# Escape pipe characters for markdown tables
|
|
60
|
-
str.gsub('|', '\\|')
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
sig { params(value: String, max_cell_bytes: Integer).returns(String) }
|
|
64
|
-
def self.truncate(value, max_cell_bytes)
|
|
65
|
-
return value if value.bytesize <= max_cell_bytes
|
|
66
|
-
|
|
67
|
-
visible = value.byteslice(0, max_cell_bytes)
|
|
68
|
-
"#{visible}...[truncated #{value.bytesize - max_cell_bytes} bytes]"
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
sig { params(value: String).returns(String) }
|
|
72
|
-
def self.normalize(value)
|
|
73
|
-
value.gsub(/\r\n?|\n/, '<br>')
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|