merge upstream and keep full message timestamps
This commit is contained in:
commit
f2be124c77
9 changed files with 44 additions and 26 deletions
|
|
@ -522,8 +522,9 @@ func (s *JSONLStore) AddMessage(
|
|||
_ context.Context, sessionKey, role, content string,
|
||||
) error {
|
||||
return s.addMsg(sessionKey, providers.Message{
|
||||
Role: role,
|
||||
Content: content,
|
||||
Role: role,
|
||||
Content: content,
|
||||
Timestamp: time.Now().Format(time.RFC3339Nano),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -539,6 +540,10 @@ func (s *JSONLStore) addMsg(sessionKey string, msg providers.Message) error {
|
|||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
if strings.TrimSpace(msg.Timestamp) == "" {
|
||||
msg.Timestamp = time.Now().Format(time.RFC3339Nano)
|
||||
}
|
||||
|
||||
// Append the message as a single JSON line.
|
||||
line, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -61,9 +61,15 @@ func TestAddMessage_BasicRoundtrip(t *testing.T) {
|
|||
if history[0].Role != "user" || history[0].Content != "hello" {
|
||||
t.Errorf("msg[0] = %+v", history[0])
|
||||
}
|
||||
if history[0].Timestamp == "" {
|
||||
t.Errorf("msg[0].Timestamp is empty")
|
||||
}
|
||||
if history[1].Role != "assistant" || history[1].Content != "hi there" {
|
||||
t.Errorf("msg[1] = %+v", history[1])
|
||||
}
|
||||
if history[1].Timestamp == "" {
|
||||
t.Errorf("msg[1].Timestamp is empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddMessage_AutoCreatesSession(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ type Attachment struct {
|
|||
type Message struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
Timestamp string `json:"timestamp,omitempty"`
|
||||
Media []string `json:"media,omitempty"`
|
||||
Attachments []Attachment `json:"attachments,omitempty"`
|
||||
ReasoningContent string `json:"reasoning_content,omitempty"`
|
||||
|
|
|
|||
|
|
@ -72,6 +72,10 @@ func (sm *SessionManager) AddFullMessage(sessionKey string, msg providers.Messag
|
|||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
|
||||
if strings.TrimSpace(msg.Timestamp) == "" {
|
||||
msg.Timestamp = time.Now().Format(time.RFC3339Nano)
|
||||
}
|
||||
|
||||
session, ok := sm.sessions[sessionKey]
|
||||
if !ok {
|
||||
session = &Session{
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ type sessionListItem struct {
|
|||
type sessionChatMessage struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
Timestamp string `json:"timestamp,omitempty"`
|
||||
Media []string `json:"media,omitempty"`
|
||||
Attachments []sessionChatAttachment `json:"attachments,omitempty"`
|
||||
}
|
||||
|
|
@ -477,6 +478,7 @@ func visibleSessionMessages(messages []providers.Message, toolFeedbackMaxArgsLen
|
|||
|
||||
for _, msg := range messages {
|
||||
attachments := sessionAttachments(msg)
|
||||
timestamp := strings.TrimSpace(msg.Timestamp)
|
||||
|
||||
switch msg.Role {
|
||||
case "tool":
|
||||
|
|
@ -486,6 +488,7 @@ func visibleSessionMessages(messages []providers.Message, toolFeedbackMaxArgsLen
|
|||
chatMsg := sessionChatMessage{
|
||||
Role: "user",
|
||||
Content: msg.Content,
|
||||
Timestamp: timestamp,
|
||||
Media: append([]string(nil), msg.Media...),
|
||||
Attachments: attachments,
|
||||
}
|
||||
|
|
@ -500,12 +503,12 @@ func visibleSessionMessages(messages []providers.Message, toolFeedbackMaxArgsLen
|
|||
continue
|
||||
}
|
||||
|
||||
toolSummaryMessages := visibleAssistantToolSummaryMessages(msg.ToolCalls, toolFeedbackMaxArgsLength)
|
||||
toolSummaryMessages := visibleAssistantToolSummaryMessages(msg.ToolCalls, timestamp, toolFeedbackMaxArgsLength)
|
||||
if len(toolSummaryMessages) > 0 {
|
||||
transcript = append(transcript, toolSummaryMessages...)
|
||||
}
|
||||
|
||||
visibleToolMessages := visibleAssistantToolMessages(msg.ToolCalls)
|
||||
visibleToolMessages := visibleAssistantToolMessages(msg.ToolCalls, timestamp)
|
||||
if len(visibleToolMessages) > 0 {
|
||||
transcript = append(transcript, visibleToolMessages...)
|
||||
}
|
||||
|
|
@ -534,6 +537,7 @@ func visibleSessionMessages(messages []providers.Message, toolFeedbackMaxArgsLen
|
|||
chatMsg := sessionChatMessage{
|
||||
Role: "assistant",
|
||||
Content: content,
|
||||
Timestamp: timestamp,
|
||||
Media: append([]string(nil), msg.Media...),
|
||||
Attachments: attachments,
|
||||
}
|
||||
|
|
@ -686,6 +690,7 @@ func assistantMessageInternalOnly(msg providers.Message) bool {
|
|||
|
||||
func visibleAssistantToolSummaryMessages(
|
||||
toolCalls []providers.ToolCall,
|
||||
timestamp string,
|
||||
toolFeedbackMaxArgsLength int,
|
||||
) []sessionChatMessage {
|
||||
if len(toolCalls) == 0 {
|
||||
|
|
@ -716,6 +721,7 @@ func visibleAssistantToolSummaryMessages(
|
|||
name,
|
||||
visibleAssistantToolSummaryText(tc, toolFeedbackMaxArgsLength),
|
||||
),
|
||||
Timestamp: timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -745,7 +751,7 @@ func visibleAssistantToolSummaryText(
|
|||
return utils.Truncate(strings.TrimSpace(argsJSON), toolFeedbackMaxArgsLength)
|
||||
}
|
||||
|
||||
func visibleAssistantToolMessages(toolCalls []providers.ToolCall) []sessionChatMessage {
|
||||
func visibleAssistantToolMessages(toolCalls []providers.ToolCall, timestamp string) []sessionChatMessage {
|
||||
if len(toolCalls) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -761,8 +767,9 @@ func visibleAssistantToolMessages(toolCalls []providers.ToolCall) []sessionChatM
|
|||
continue
|
||||
}
|
||||
messages = append(messages, sessionChatMessage{
|
||||
Role: "assistant",
|
||||
Content: content,
|
||||
Role: "assistant",
|
||||
Content: content,
|
||||
Timestamp: timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -166,8 +166,8 @@ func TestHandleGetSession_JSONLStorage(t *testing.T) {
|
|||
|
||||
sessionKey := legacyPicoSessionPrefix + "detail-jsonl"
|
||||
for _, msg := range []providers.Message{
|
||||
{Role: "user", Content: "first"},
|
||||
{Role: "assistant", Content: "second"},
|
||||
{Role: "user", Content: "first", Timestamp: "2026-04-23T11:05:00+08:00"},
|
||||
{Role: "assistant", Content: "second", Timestamp: "2026-04-23T11:06:00+08:00"},
|
||||
{Role: "tool", Content: "ignored"},
|
||||
} {
|
||||
if err := store.AddFullMessage(nil, sessionKey, msg); err != nil {
|
||||
|
|
@ -194,8 +194,9 @@ func TestHandleGetSession_JSONLStorage(t *testing.T) {
|
|||
ID string `json:"id"`
|
||||
Summary string `json:"summary"`
|
||||
Messages []struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
} `json:"messages"`
|
||||
}
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
||||
|
|
@ -213,9 +214,15 @@ func TestHandleGetSession_JSONLStorage(t *testing.T) {
|
|||
if resp.Messages[0].Role != "user" || resp.Messages[0].Content != "first" {
|
||||
t.Fatalf("first message = %#v, want user/first", resp.Messages[0])
|
||||
}
|
||||
if resp.Messages[0].Timestamp != "2026-04-23T11:05:00+08:00" {
|
||||
t.Fatalf("resp.Messages[0].Timestamp = %q, want %q", resp.Messages[0].Timestamp, "2026-04-23T11:05:00+08:00")
|
||||
}
|
||||
if resp.Messages[1].Role != "assistant" || resp.Messages[1].Content != "second" {
|
||||
t.Fatalf("second message = %#v, want assistant/second", resp.Messages[1])
|
||||
}
|
||||
if resp.Messages[1].Timestamp != "2026-04-23T11:06:00+08:00" {
|
||||
t.Fatalf("resp.Messages[1].Timestamp = %q, want %q", resp.Messages[1].Timestamp, "2026-04-23T11:06:00+08:00")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleGetSession_HidesHandledToolAttachmentsBackedByMediaRefs(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export interface SessionDetail {
|
|||
messages: {
|
||||
role: "user" | "assistant"
|
||||
content: string
|
||||
timestamp?: string
|
||||
media?: string[]
|
||||
attachments?: {
|
||||
type?: "image" | "audio" | "video" | "file"
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export async function loadSessionMessages(
|
|||
media: message.media,
|
||||
attachments: message.attachments,
|
||||
}),
|
||||
timestamp: fallbackTime,
|
||||
timestamp: message.timestamp?.trim() || fallbackTime,
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,20 +38,7 @@ export function formatMessageTime(dateRaw: number | string | Date): string {
|
|||
if (!date.isValid()) {
|
||||
return ""
|
||||
}
|
||||
const now = dayjs()
|
||||
|
||||
const isToday = date.isSame(now, "day")
|
||||
const isThisYear = date.isSame(now, "year")
|
||||
|
||||
if (isToday) {
|
||||
return date.format("LT")
|
||||
}
|
||||
|
||||
if (isThisYear) {
|
||||
return date.format("MMM D LT")
|
||||
}
|
||||
|
||||
return date.format("ll LT")
|
||||
return date.format("YYYY-MM-DD HH:mm")
|
||||
}
|
||||
|
||||
export function usePicoChat() {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue