Packages
nous
0.16.4
0.17.0
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.17
0.12.16
0.12.15
0.12.14
0.12.13
0.12.12
0.12.11
0.12.9
0.12.7
0.12.6
0.12.5
0.12.3
0.12.2
0.12.0
0.11.3
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.5.0
AI agent framework for Elixir with multi-provider LLM support
Current section
Files
Jump to
Current section
Files
lib/nous/eval/reporter/json.ex
defmodule Nous.Eval.Reporter.Json do
@moduledoc """
JSON output for evaluation reports.
"""
alias Nous.Eval.SuiteResult
@doc """
Generate JSON report.
"""
@spec to_json(SuiteResult.t()) :: String.t()
def to_json(%SuiteResult{} = result) do
result
|> to_map()
|> Nous.JSON.pretty_encode!()
end
@doc """
Write report to JSON file.
"""
@spec to_file(SuiteResult.t(), String.t()) :: :ok | {:error, term()}
def to_file(%SuiteResult{} = result, path) do
json = to_json(result)
# Ensure directory exists
dir = Path.dirname(path)
File.mkdir_p!(dir)
case File.write(path, json) do
:ok -> :ok
{:error, reason} -> {:error, {:write_error, path, reason}}
end
end
@doc """
Convert result to a map suitable for JSON encoding.
"""
@spec to_map(SuiteResult.t()) :: map()
def to_map(%SuiteResult{} = result) do
%{
suite_name: result.suite_name,
started_at: DateTime.to_iso8601(result.started_at),
completed_at: DateTime.to_iso8601(result.completed_at),
duration_ms: result.duration_ms,
summary: %{
total: result.total_count,
passed: result.pass_count,
failed: result.fail_count,
errors: result.error_count,
pass_rate: result.pass_rate,
aggregate_score: result.aggregate_score
},
metrics: format_metrics(result.metrics_summary),
results: Enum.map(result.results, &format_result/1)
}
end
defp format_metrics(nil), do: nil
defp format_metrics(metrics) do
%{
count: metrics.count,
scores: %{
mean: metrics.mean_score,
min: metrics.min_score,
max: metrics.max_score
},
tokens: %{
total: metrics.total_tokens,
mean: metrics.mean_tokens,
p50: metrics.p50_tokens,
p95: metrics.p95_tokens,
p99: metrics.p99_tokens
},
latency_ms: %{
mean: metrics.mean_latency_ms,
p50: metrics.p50_latency_ms,
p95: metrics.p95_latency_ms,
p99: metrics.p99_latency_ms
},
tools: %{
total_calls: metrics.total_tool_calls,
distribution: metrics.tool_call_distribution,
error_rate: metrics.tool_error_rate
},
cost: %{
total: metrics.total_estimated_cost,
mean_per_run: metrics.mean_cost_per_run
}
}
end
defp format_result(result) do
base = %{
test_case_id: result.test_case_id,
test_case_name: result.test_case_name,
passed: result.passed,
score: result.score,
duration_ms: result.duration_ms,
run_at: format_datetime(result.run_at)
}
if result.error do
Map.merge(base, %{
error: inspect(result.error)
})
else
Map.merge(base, %{
actual_output: format_output(result.actual_output),
expected_output: format_output(result.expected_output),
evaluation_details: result.evaluation_details,
metrics: format_run_metrics(result.metrics)
})
end
end
defp format_output(output) when is_binary(output), do: output
defp format_output(output), do: inspect(output)
defp format_datetime(nil), do: nil
defp format_datetime(%DateTime{} = dt), do: DateTime.to_iso8601(dt)
defp format_run_metrics(nil), do: nil
defp format_run_metrics(metrics) do
%{
input_tokens: metrics.input_tokens,
output_tokens: metrics.output_tokens,
total_tokens: metrics.total_tokens,
total_duration_ms: metrics.total_duration_ms,
iterations: metrics.iterations,
tool_calls: metrics.tool_calls,
requests: metrics.requests,
tools_used: metrics.tools_used,
estimated_cost: metrics.estimated_cost
}
end
end