Prompt history: list preview shows the message as written, not the engine's time line (#660)
Version 0.1.13. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
ClawdDotNet.Core.BuildInfo bleibt daneben bestehen: das ist ein von Hand
|
||||
gefuehrter Zaehler mit Aenderungstext, keine Versionsangabe.
|
||||
-->
|
||||
<Version>0.1.12</Version>
|
||||
<Version>0.1.13</Version>
|
||||
|
||||
<!--
|
||||
Symbole in die Assembly einbetten statt als .pdb daneben zu legen.
|
||||
|
||||
@@ -217,7 +217,7 @@ public sealed class AgentEngine : IAgentMessageRouter, Staging.IFrozenCallExecut
|
||||
messages.Add(ChatMessage.System(systemPrompt));
|
||||
|
||||
messages.Add(ChatMessage.User(MitZeit(userMessage, source)));
|
||||
recorder.Begin(systemPrompt, messages[^1].Content?.ToString() ?? userMessage);
|
||||
recorder.Begin(systemPrompt, messages[^1].Content ?? userMessage, userMessage);
|
||||
|
||||
string? finalMessage = null;
|
||||
var truncated = false;
|
||||
@@ -556,7 +556,8 @@ public sealed class AgentEngine : IAgentMessageRouter, Staging.IFrozenCallExecut
|
||||
AddChatEntry(agentConfig.AgentId, "user", userMessage, source);
|
||||
recorder.Begin(
|
||||
messages.Count > 0 && messages[0].Role == "system" ? messages[0].Content?.ToString() : null,
|
||||
messages[^1].Content?.ToString() ?? userMessage);
|
||||
messages[^1].Content ?? userMessage,
|
||||
userMessage);
|
||||
|
||||
string? finalMessage = null;
|
||||
var truncated = false;
|
||||
|
||||
@@ -64,6 +64,10 @@ public sealed record RunTranscript
|
||||
public DateTime? CompletedAt { get; init; }
|
||||
public string Status { get; init; } = "";
|
||||
public string? SystemPromptHash { get; init; }
|
||||
|
||||
/// <summary>The task or message as written, for lists. Falls back to the first user entry.</summary>
|
||||
public string? PromptPreview { get; init; }
|
||||
|
||||
public IReadOnlyList<TranscriptEntry> Entries { get; init; } = [];
|
||||
|
||||
/// <summary>Characters stored, after clipping.</summary>
|
||||
|
||||
@@ -34,6 +34,9 @@ public sealed class RunTranscriptRecorder
|
||||
|
||||
public bool Enabled { get; }
|
||||
public string? SystemPrompt { get; private set; }
|
||||
|
||||
/// <summary>The message as sent by its author - without the time line the engine prepends.</summary>
|
||||
public string? PromptPreview { get; private set; }
|
||||
public DateTime StartedAt { get; }
|
||||
|
||||
public RunTranscriptRecorder(bool enabled = true, TimeProvider? clock = null)
|
||||
@@ -45,11 +48,14 @@ public sealed class RunTranscriptRecorder
|
||||
|
||||
// ─── Engine hooks ───
|
||||
|
||||
public void Begin(string? systemPrompt, string userMessage)
|
||||
/// <param name="sentMessage">What the model received, including what the engine added.</param>
|
||||
/// <param name="originalMessage">The message as its author wrote it - the list preview.</param>
|
||||
public void Begin(string? systemPrompt, string sentMessage, string? originalMessage = null)
|
||||
{
|
||||
if (!Enabled) return;
|
||||
SystemPrompt = string.IsNullOrWhiteSpace(systemPrompt) ? null : SecretScrubber.Scrub(systemPrompt);
|
||||
Add(TranscriptEntryKind.User, userMessage, MaxTextChars);
|
||||
PromptPreview = SecretScrubber.Scrub(originalMessage ?? sentMessage);
|
||||
Add(TranscriptEntryKind.User, sentMessage, MaxTextChars);
|
||||
}
|
||||
|
||||
/// <summary>A message the engine sent on its own (follow-up after an empty answer etc.).</summary>
|
||||
@@ -127,6 +133,7 @@ public sealed class RunTranscriptRecorder
|
||||
CompletedAt = _clock.GetUtcNow().UtcDateTime,
|
||||
Status = status,
|
||||
SystemPromptHash = SystemPrompt is null ? null : Hash(SystemPrompt),
|
||||
PromptPreview = PromptPreview,
|
||||
Entries = _entries.ToList(),
|
||||
SizeChars = _size,
|
||||
Truncated = _truncated
|
||||
|
||||
@@ -69,7 +69,7 @@ public sealed class SqliteRunTranscriptStore(SqliteStorage storage) : IRunTransc
|
||||
cmd.Parameters.AddWithValue("@completed", t.CompletedAt is { } c ? Format(c) : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@status", t.Status);
|
||||
cmd.Parameters.AddWithValue("@hash", (object?)t.SystemPromptHash ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@prompt", Snippet(t.Entries.FirstOrDefault(e => e.Kind == TranscriptEntryKind.User)?.Text));
|
||||
cmd.Parameters.AddWithValue("@prompt", Snippet(t.PromptPreview ?? t.Entries.FirstOrDefault(e => e.Kind == TranscriptEntryKind.User)?.Text));
|
||||
cmd.Parameters.AddWithValue("@response", Snippet(t.Entries.LastOrDefault(e => e.Kind == TranscriptEntryKind.Assistant && !string.IsNullOrWhiteSpace(e.Text))?.Text));
|
||||
cmd.Parameters.AddWithValue("@entries", JsonSerializer.Serialize(t.Entries, Json));
|
||||
cmd.Parameters.AddWithValue("@size", t.SizeChars);
|
||||
|
||||
@@ -207,6 +207,7 @@ public sealed class PromptHistoryTests : IDisposable
|
||||
t.AgentId.ShouldBe("wetterdienst");
|
||||
t.Entries.Select(e => e.Kind).ShouldBe(["user", "assistant", "tool_result", "assistant"]);
|
||||
t.Entries[0].Text.ShouldContain("Wie ist das Wetter?");
|
||||
items.Single().Prompt.ShouldBe("Wie ist das Wetter?", "the list shows the message, not the time line the engine adds");
|
||||
t.Entries[2].Text.ShouldNotBeNull().ShouldContain("18 °C", Case.Sensitive, "stored as the model saw it, frame included");
|
||||
t.Entries[3].Text.ShouldBe("Es sind 18 °C.");
|
||||
(await _store.GetSystemPromptAsync(t.SystemPromptHash!, default)).ShouldNotBeNull().ShouldContain("wetterdienst");
|
||||
|
||||
Reference in New Issue
Block a user