merge: run reporting — token accounting + populate empty result
This commit is contained in:
@@ -44,6 +44,14 @@ public sealed class StreamAnalyzer
|
||||
_structuredOutputJson = structuredProp.ToString();
|
||||
if (root.TryGetProperty("session_id", out var sessionProp))
|
||||
_sessionId = sessionProp.GetString();
|
||||
// Authoritative token totals live on the result event.
|
||||
if (root.TryGetProperty("usage", out var resultUsage))
|
||||
{
|
||||
if (resultUsage.TryGetProperty("input_tokens", out var inp))
|
||||
_tokensIn = inp.GetInt32();
|
||||
if (resultUsage.TryGetProperty("output_tokens", out var outp))
|
||||
_tokensOut = outp.GetInt32();
|
||||
}
|
||||
break;
|
||||
|
||||
case "assistant":
|
||||
@@ -66,7 +74,7 @@ public sealed class StreamAnalyzer
|
||||
|
||||
public StreamResult GetResult() => new()
|
||||
{
|
||||
ResultMarkdown = _resultMarkdown,
|
||||
ResultMarkdown = FallbackResult(),
|
||||
StructuredOutputJson = _structuredOutputJson,
|
||||
SessionId = _sessionId,
|
||||
TurnCount = _turnCount,
|
||||
@@ -75,6 +83,20 @@ public sealed class StreamAnalyzer
|
||||
ApiRetryCount = _apiRetryCount,
|
||||
};
|
||||
|
||||
private string? FallbackResult()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_resultMarkdown)) return _resultMarkdown;
|
||||
if (_structuredOutputJson is null) return _resultMarkdown;
|
||||
try
|
||||
{
|
||||
using var doc = JsonDocument.Parse(_structuredOutputJson);
|
||||
if (doc.RootElement.TryGetProperty("summary", out var s))
|
||||
return s.GetString();
|
||||
}
|
||||
catch { }
|
||||
return _structuredOutputJson;
|
||||
}
|
||||
|
||||
private void TryAccumulateUsage(JsonElement root)
|
||||
{
|
||||
if (!root.TryGetProperty("event", out var eventProp)) return;
|
||||
|
||||
@@ -79,4 +79,45 @@ public sealed class StreamAnalyzerTests
|
||||
Assert.Null(result.ResultMarkdown);
|
||||
Assert.Null(result.SessionId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Token_Usage_From_Result_Event()
|
||||
{
|
||||
var analyzer = new StreamAnalyzer();
|
||||
analyzer.ProcessLine("""{"type":"result","result":"done","session_id":"s1","usage":{"input_tokens":150,"output_tokens":75,"cache_read_input_tokens":0}}""");
|
||||
var result = analyzer.GetResult();
|
||||
Assert.Equal(150, result.TokensIn);
|
||||
Assert.Equal(75, result.TokensOut);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Result_Usage_Overrides_Stream_Event_Accumulation()
|
||||
{
|
||||
var analyzer = new StreamAnalyzer();
|
||||
analyzer.ProcessLine("""{"type":"stream_event","event":{"type":"message_start","message":{"usage":{"input_tokens":10,"output_tokens":5}}}}""");
|
||||
analyzer.ProcessLine("""{"type":"result","result":"done","session_id":"s1","usage":{"input_tokens":200,"output_tokens":90}}""");
|
||||
var result = analyzer.GetResult();
|
||||
Assert.Equal(200, result.TokensIn);
|
||||
Assert.Equal(90, result.TokensOut);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Empty_Result_Falls_Back_To_Structured_Output_Summary()
|
||||
{
|
||||
var analyzer = new StreamAnalyzer();
|
||||
analyzer.ProcessLine("""{"type":"result","result":"","structured_output":{"summary":"Task completed successfully.","data":{}},"session_id":"s1"}""");
|
||||
var result = analyzer.GetResult();
|
||||
Assert.Equal("Task completed successfully.", result.ResultMarkdown);
|
||||
Assert.Contains("summary", result.StructuredOutputJson);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Empty_Result_Falls_Back_To_Full_Json_When_No_Summary()
|
||||
{
|
||||
var analyzer = new StreamAnalyzer();
|
||||
analyzer.ProcessLine("""{"type":"result","result":"","structured_output":{"output":"42"},"session_id":"s1"}""");
|
||||
var result = analyzer.GetResult();
|
||||
Assert.Contains("output", result.ResultMarkdown);
|
||||
Assert.Contains("42", result.ResultMarkdown);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user