feat: foundations — canonical types, Parse grammar, env DSNs, health, chains
Phase 1 of the majordomo build: - llm/ canonical contract (messages, parts, tools, capabilities, streaming, Model/Provider, error classification) - health/ clock-injected tracker (threshold bench, exponential capped cooldown, reset-on-success) - root Registry + Parse (verbatim model ids, inline recursive alias expansion with cycle detection, chain dedup), LLM_* env-DSN providers (go-llm parity: lazy fallback + eager LoadEnv), health-aware chain executor behind the Model interface - provider/fake scriptable test provider; hermetic test suite incl. the trailing-thinking chain and foreman:// env loading - ADRs 0001-0008, CLAUDE.md, README (honest matrix), CI workflow, docs/phase-1-design.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
package llm
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMessageText(t *testing.T) {
|
||||
m := UserParts(Text("a "), Image("image/png", []byte{1}), Text("b"))
|
||||
if got := m.Text(); got != "a b" {
|
||||
t.Errorf("Text = %q, want %q", got, "a b")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConstructors(t *testing.T) {
|
||||
if m := SystemText("s"); m.Role != RoleSystem || m.Text() != "s" {
|
||||
t.Errorf("SystemText = %+v", m)
|
||||
}
|
||||
if m := UserText("u"); m.Role != RoleUser || m.Text() != "u" {
|
||||
t.Errorf("UserText = %+v", m)
|
||||
}
|
||||
if m := AssistantText("a"); m.Role != RoleAssistant || m.Text() != "a" {
|
||||
t.Errorf("AssistantText = %+v", m)
|
||||
}
|
||||
m := ToolResultsMessage(ToolResult{ID: "1", Content: "ok"})
|
||||
if m.Role != RoleTool || len(m.ToolResults) != 1 {
|
||||
t.Errorf("ToolResultsMessage = %+v", m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResponseTextAndMessage(t *testing.T) {
|
||||
r := &Response{
|
||||
Parts: []Part{Text("hello "), Text("world")},
|
||||
ToolCalls: []ToolCall{{ID: "1", Name: "t"}},
|
||||
}
|
||||
if got := r.Text(); got != "hello world" {
|
||||
t.Errorf("Text = %q", got)
|
||||
}
|
||||
m := r.Message()
|
||||
if m.Role != RoleAssistant || m.Text() != "hello world" || len(m.ToolCalls) != 1 {
|
||||
t.Errorf("Message = %+v", m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUsageAccumulation(t *testing.T) {
|
||||
u := Usage{InputTokens: 10, OutputTokens: 5}
|
||||
u.Add(Usage{InputTokens: 1, OutputTokens: 2})
|
||||
if u.InputTokens != 11 || u.OutputTokens != 7 || u.Total() != 18 {
|
||||
t.Errorf("usage = %+v", u)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCapabilitiesHelpers(t *testing.T) {
|
||||
c := Capabilities{}
|
||||
if c.SupportsImages() {
|
||||
t.Error("zero MaxImagesPerReq must mean images unsupported")
|
||||
}
|
||||
if !c.MIMEAllowed("image/png") {
|
||||
t.Error("empty AllowedImageMIME must allow any type")
|
||||
}
|
||||
c = Capabilities{MaxImagesPerReq: 2, AllowedImageMIME: []string{"image/jpeg"}}
|
||||
if !c.SupportsImages() || c.MIMEAllowed("image/png") || !c.MIMEAllowed("image/jpeg") {
|
||||
t.Errorf("capabilities helpers misbehave: %+v", c)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user