Integrate LLM client with blog generation (Phase 10)
- Add LLMBaseURL and LLMAPIKey config options for narrative generation - Wire up LLM client to generateBlog() when LLM is configured - Fix ParticipantData type usage in test files - Simplify rivalry arc detection (remove alternation check) - Fix type conversion in upset detection gap calculation - Mark narrative engine as complete in PROGRESS.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
44544622ae
commit
5356c8ee0a
5 changed files with 19 additions and 15 deletions
|
|
@ -455,7 +455,7 @@
|
|||
- Frontend fetches from R2 (`https://r2.aicodebattle.com/evolution/live.json`)
|
||||
- Cache-Control: max-age=10 for near-real-time updates
|
||||
- Tests for R2 config validation and credential handling
|
||||
- [ ] Narrative engine (weekly story arc detection + LLM chronicles)
|
||||
- [x] Narrative engine (weekly story arc detection + LLM chronicles)
|
||||
- [x] Public match data documentation (OpenAPI-style)
|
||||
- New `/docs/api` route with comprehensive endpoint documentation
|
||||
- Documents Pages, R2, and B2 static JSON endpoints
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@ func LoadConfig() *Config {
|
|||
B2BucketName: os.Getenv("ACB_B2_BUCKET"),
|
||||
|
||||
OutputDir: getEnv("ACB_OUTPUT_DIR", "/tmp/acb-index"),
|
||||
|
||||
LLMBaseURL: getEnv("ACB_LLM_BASE_URL", ""),
|
||||
LLMAPIKey: os.Getenv("ACB_LLM_API_KEY"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -147,7 +147,11 @@ func runBuildCycle(ctx context.Context, db *sql.DB, cfg *Config) error {
|
|||
}
|
||||
|
||||
// Generate blog posts (weekly meta reports and chronicles)
|
||||
if err := generateBlog(data, cfg.OutputDir); err != nil {
|
||||
var llmClient *LLMClient
|
||||
if cfg.LLMBaseURL != "" {
|
||||
llmClient = NewLLMClient(cfg.LLMBaseURL, cfg.LLMAPIKey)
|
||||
}
|
||||
if err := generateBlog(data, cfg.OutputDir, llmClient); err != nil {
|
||||
slog.Error("Failed to generate blog", "error", err)
|
||||
// Non-fatal - continue with rest of build
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ func TestGenerateMatchIndex(t *testing.T) {
|
|||
TurnCount: 200,
|
||||
EndCondition: "elimination",
|
||||
CompletedAt: now,
|
||||
Participants: []MatchParticipant{
|
||||
Participants: []ParticipantData{
|
||||
{BotID: "bot1", Score: 5, Won: true},
|
||||
{BotID: "bot2", Score: 2, Won: false},
|
||||
},
|
||||
|
|
@ -246,7 +246,7 @@ func TestGeneratePlaylists(t *testing.T) {
|
|||
TurnCount: 200,
|
||||
EndCondition: "elimination",
|
||||
CompletedAt: now,
|
||||
Participants: []MatchParticipant{
|
||||
Participants: []ParticipantData{
|
||||
{BotID: "bot1", Score: 3, Won: true},
|
||||
{BotID: "bot2", Score: 2, Won: false}, // Close finish (diff = 1)
|
||||
},
|
||||
|
|
@ -257,7 +257,7 @@ func TestGeneratePlaylists(t *testing.T) {
|
|||
TurnCount: 150,
|
||||
EndCondition: "dominance",
|
||||
CompletedAt: now.Add(-time.Hour),
|
||||
Participants: []MatchParticipant{
|
||||
Participants: []ParticipantData{
|
||||
{BotID: "bot1", Score: 0, Won: false},
|
||||
{BotID: "bot2", Score: 10, Won: true}, // Not close (diff = 10)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -41,6 +41,11 @@ type StoryArc struct {
|
|||
ParentIDs []string `json:"parent_ids,omitempty"`
|
||||
Generation int `json:"generation,omitempty"`
|
||||
CommunityHint string `json:"community_hint,omitempty"`
|
||||
|
||||
// Rivalry-specific fields
|
||||
BotAWins int `json:"bot_a_wins,omitempty"`
|
||||
BotBWins int `json:"bot_b_wins,omitempty"`
|
||||
TotalMatches int `json:"total_matches,omitempty"`
|
||||
}
|
||||
|
||||
// KeyMatch represents a key match for narrative context
|
||||
|
|
@ -430,17 +435,13 @@ func detectRivalryArcs(data *IndexData) []StoryArc {
|
|||
}
|
||||
botAID, botBID := parts[0], parts[1]
|
||||
|
||||
// Count wins for each bot and check alternation
|
||||
// Count wins for each bot
|
||||
botAWins := 0
|
||||
botBWins := 0
|
||||
alternating := true
|
||||
lastWinner := ""
|
||||
|
||||
for _, m := range matches {
|
||||
var winnerID string
|
||||
for _, p := range m.Participants {
|
||||
if p.Won {
|
||||
winnerID = p.BotID
|
||||
if p.BotID == botAID {
|
||||
botAWins++
|
||||
} else if p.BotID == botBID {
|
||||
|
|
@ -449,10 +450,6 @@ func detectRivalryArcs(data *IndexData) []StoryArc {
|
|||
break
|
||||
}
|
||||
}
|
||||
if lastWinner != "" && winnerID == lastWinner {
|
||||
alternating = false
|
||||
}
|
||||
lastWinner = winnerID
|
||||
}
|
||||
|
||||
// Only include if wins are reasonably close (not one-sided)
|
||||
|
|
@ -500,7 +497,7 @@ func detectUpsetArcs(data *IndexData) []StoryArc {
|
|||
}
|
||||
|
||||
// Check if underdog won (winner had lower rating)
|
||||
gap := loser.PreMatchRating - winner.PreMatchRating
|
||||
gap := int(loser.PreMatchRating - winner.PreMatchRating)
|
||||
if gap > biggestGap {
|
||||
biggestGap = gap
|
||||
biggestUpset = &StoryArc{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue