ollie/format/format.go

88 lines
2.4 KiB
Go

package format
import (
"fmt"
"strings"
)
// Formatting constants for the 9P chat log protocol.
// Used by fs.NewEventHandler and fs.replayMessagesToLog.
const (
// EndTag marks the end of a message block.
EndTag = "[[[end]]]"
// RoleTag formats a role delimiter, e.g. "[[[user]]]".
RoleTag = "[[[%s]]]"
// ToolFence is the opening fence for tool output blocks.
ToolFence = "```\n"
// AssistantHeaderPrefix is prepended to assistant headers when a response ID is present.
AssistantHeaderPrefix = "[[[assistant:"
// AssistantTag is the bare assistant header (no response ID).
AssistantTag = "[[[assistant]]]"
// UserTag is the user role delimiter.
UserTag = "[[[user]]]"
// RetryTag is the retry role delimiter.
RetryTag = "[[[retry]]]"
// ToolDelimPrefix is the prefix for tool delimiters, e.g. "[[[tool:shell]]]".
ToolDelimPrefix = "[[[tool:"
// ToolTag is the bare tool role delimiter (no name).
ToolTag = "[[[tool]]]"
// CallDelimPrefix is the prefix for call delimiters, e.g. "[[[call:file_read]]]".
CallDelimPrefix = "[[[call:"
)
// RoleDelim returns a formatted role delimiter.
func RoleDelim(role string) string {
return fmt.Sprintf(RoleTag, role)
}
// ToolDelim returns a formatted tool delimiter.
func ToolDelim(name string) string {
return fmt.Sprintf(ToolDelimPrefix+"%s]]]\n", name)
}
// CallDelim returns a formatted call delimiter.
func CallDelim(name string) string {
return fmt.Sprintf(CallDelimPrefix+"%s]]]\n", name)
}
// BlockHeader is a single-line [[[...]]] marker that opens a log block.
type BlockHeader struct {
Role string // e.g. "user", "assistant", "tool", "retry", or "tool:shell"
Name string // for tool/call: the sub-name (e.g. "shell"), empty for bare roles
}
// ParseBlockHeader extracts a BlockHeader from a line like "[[[tool:shell]]]"
// or "[[[assistant]]]". Returns nil if the line is not a valid block header.
func ParseBlockHeader(line string) *BlockHeader {
const prefix, suffix = "[[[", "]]]"
if !strings.HasPrefix(line, prefix) || !strings.HasSuffix(line, suffix) {
return nil
}
inner := strings.TrimPrefix(line, prefix)
inner = strings.TrimSuffix(inner, suffix)
if inner == "" {
return nil
}
bh := &BlockHeader{Role: inner}
if idx := strings.Index(inner, ":"); idx >= 0 {
bh.Role = inner[:idx]
bh.Name = inner[idx+1:]
}
return bh
}
// IsEndMarker reports whether the line is exactly the [[[end]]] closing marker.
func IsEndMarker(line string) bool {
return line == EndTag
}