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
|
ClawdDotNet.Core.BuildInfo bleibt daneben bestehen: das ist ein von Hand
|
||||||
gefuehrter Zaehler mit Aenderungstext, keine Versionsangabe.
|
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.
|
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.System(systemPrompt));
|
||||||
|
|
||||||
messages.Add(ChatMessage.User(MitZeit(userMessage, source)));
|
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;
|
string? finalMessage = null;
|
||||||
var truncated = false;
|
var truncated = false;
|
||||||
@@ -556,7 +556,8 @@ public sealed class AgentEngine : IAgentMessageRouter, Staging.IFrozenCallExecut
|
|||||||
AddChatEntry(agentConfig.AgentId, "user", userMessage, source);
|
AddChatEntry(agentConfig.AgentId, "user", userMessage, source);
|
||||||
recorder.Begin(
|
recorder.Begin(
|
||||||
messages.Count > 0 && messages[0].Role == "system" ? messages[0].Content?.ToString() : null,
|
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;
|
string? finalMessage = null;
|
||||||
var truncated = false;
|
var truncated = false;
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ public sealed record RunTranscript
|
|||||||
public DateTime? CompletedAt { get; init; }
|
public DateTime? CompletedAt { get; init; }
|
||||||
public string Status { get; init; } = "";
|
public string Status { get; init; } = "";
|
||||||
public string? SystemPromptHash { 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; } = [];
|
public IReadOnlyList<TranscriptEntry> Entries { get; init; } = [];
|
||||||
|
|
||||||
/// <summary>Characters stored, after clipping.</summary>
|
/// <summary>Characters stored, after clipping.</summary>
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ public sealed class RunTranscriptRecorder
|
|||||||
|
|
||||||
public bool Enabled { get; }
|
public bool Enabled { get; }
|
||||||
public string? SystemPrompt { get; private set; }
|
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 DateTime StartedAt { get; }
|
||||||
|
|
||||||
public RunTranscriptRecorder(bool enabled = true, TimeProvider? clock = null)
|
public RunTranscriptRecorder(bool enabled = true, TimeProvider? clock = null)
|
||||||
@@ -45,11 +48,14 @@ public sealed class RunTranscriptRecorder
|
|||||||
|
|
||||||
// ─── Engine hooks ───
|
// ─── 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;
|
if (!Enabled) return;
|
||||||
SystemPrompt = string.IsNullOrWhiteSpace(systemPrompt) ? null : SecretScrubber.Scrub(systemPrompt);
|
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>
|
/// <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,
|
CompletedAt = _clock.GetUtcNow().UtcDateTime,
|
||||||
Status = status,
|
Status = status,
|
||||||
SystemPromptHash = SystemPrompt is null ? null : Hash(SystemPrompt),
|
SystemPromptHash = SystemPrompt is null ? null : Hash(SystemPrompt),
|
||||||
|
PromptPreview = PromptPreview,
|
||||||
Entries = _entries.ToList(),
|
Entries = _entries.ToList(),
|
||||||
SizeChars = _size,
|
SizeChars = _size,
|
||||||
Truncated = _truncated
|
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("@completed", t.CompletedAt is { } c ? Format(c) : DBNull.Value);
|
||||||
cmd.Parameters.AddWithValue("@status", t.Status);
|
cmd.Parameters.AddWithValue("@status", t.Status);
|
||||||
cmd.Parameters.AddWithValue("@hash", (object?)t.SystemPromptHash ?? DBNull.Value);
|
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("@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("@entries", JsonSerializer.Serialize(t.Entries, Json));
|
||||||
cmd.Parameters.AddWithValue("@size", t.SizeChars);
|
cmd.Parameters.AddWithValue("@size", t.SizeChars);
|
||||||
|
|||||||
@@ -207,6 +207,7 @@ public sealed class PromptHistoryTests : IDisposable
|
|||||||
t.AgentId.ShouldBe("wetterdienst");
|
t.AgentId.ShouldBe("wetterdienst");
|
||||||
t.Entries.Select(e => e.Kind).ShouldBe(["user", "assistant", "tool_result", "assistant"]);
|
t.Entries.Select(e => e.Kind).ShouldBe(["user", "assistant", "tool_result", "assistant"]);
|
||||||
t.Entries[0].Text.ShouldContain("Wie ist das Wetter?");
|
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[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.");
|
t.Entries[3].Text.ShouldBe("Es sind 18 °C.");
|
||||||
(await _store.GetSystemPromptAsync(t.SystemPromptHash!, default)).ShouldNotBeNull().ShouldContain("wetterdienst");
|
(await _store.GetSystemPromptAsync(t.SystemPromptHash!, default)).ShouldNotBeNull().ShouldContain("wetterdienst");
|
||||||
|
|||||||
Reference in New Issue
Block a user