fix: remove unused imports in evolver, misc pre-dispatch changes
Remove unused encoding/json and net/http imports from cmd/acb-evolver/run.go that caused build failure. Include other pre-dispatch changes from prior work. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
a509b70800
commit
38f14e1997
4 changed files with 95 additions and 11 deletions
|
|
@ -65,6 +65,16 @@ type MetaDescription struct {
|
|||
IslandStats map[string]IslandStat
|
||||
}
|
||||
|
||||
// CommunityHint is a high-upvote tactical observation from the replay viewer
|
||||
// (§13.6), consumed by the evolver to ground prompts in player feedback.
|
||||
type CommunityHint struct {
|
||||
MatchID string
|
||||
Turn int
|
||||
Type string // "idea" or "mistake"
|
||||
Body string
|
||||
Upvotes int
|
||||
}
|
||||
|
||||
// Request bundles everything the prompt builder needs to produce a prompt.
|
||||
type Request struct {
|
||||
// Parents are the programs selected as evolutionary parents.
|
||||
|
|
@ -73,6 +83,8 @@ type Request struct {
|
|||
Replays []MatchSummary
|
||||
// Meta describes the current competitive landscape.
|
||||
Meta MetaDescription
|
||||
// CommunityHints are high-upvote tactical insights from §13.6 feedback.
|
||||
CommunityHints []CommunityHint
|
||||
// Island is the island this candidate will compete on.
|
||||
Island string
|
||||
// TargetLang is the programming language for the evolved bot
|
||||
|
|
@ -93,6 +105,7 @@ func Assemble(r Request) string {
|
|||
writeIslandContext(&sb, r.Island, r.Generation)
|
||||
writeMetaSection(&sb, r.Meta)
|
||||
writeReplaySection(&sb, r.Replays)
|
||||
writeCommunityHintsSection(&sb, r.CommunityHints)
|
||||
writeParentSection(&sb, r.Parents)
|
||||
if r.TaskOverride != "" {
|
||||
sb.WriteString("## Task\n")
|
||||
|
|
@ -104,6 +117,17 @@ func Assemble(r Request) string {
|
|||
return sb.String()
|
||||
}
|
||||
|
||||
func writeCommunityHintsSection(sb *strings.Builder, hints []CommunityHint) {
|
||||
if len(hints) == 0 {
|
||||
return
|
||||
}
|
||||
sb.WriteString("## Community Tactical Insights (from replay annotations)\n\n")
|
||||
for _, h := range hints {
|
||||
fmt.Fprintf(sb, "Replay %s, Turn %d (%d upvotes):\n", h.MatchID, h.Turn, h.Upvotes)
|
||||
fmt.Fprintf(sb, "%q\n\n", h.Body)
|
||||
}
|
||||
}
|
||||
|
||||
func writeSystemContext(sb *strings.Builder, targetLang string) {
|
||||
sb.WriteString("You are an AI bot evolution engine for a competitive grid strategy game.\n")
|
||||
sb.WriteString("Your task is to write an improved bot strategy in ")
|
||||
|
|
|
|||
|
|
@ -82,6 +82,10 @@ type RunConfig struct {
|
|||
|
||||
// Languages to evolve (in priority order)
|
||||
Languages []string
|
||||
|
||||
// PagesBaseURL is the Cloudflare Pages base URL for reading static indexes
|
||||
// such as community_hints.json. Empty disables community hint loading.
|
||||
PagesBaseURL string
|
||||
}
|
||||
|
||||
// DefaultRunConfig returns production-ready defaults.
|
||||
|
|
|
|||
|
|
@ -125,6 +125,45 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
// uploadMetaJSONToR2 uploads the generated static meta JSON files to R2 so
|
||||
// they are available at the R2 CDN URL in addition to the Pages deploy.
|
||||
func uploadMetaJSONToR2(ctx context.Context, cfg *Config, outputDir string, data *IndexData) error {
|
||||
if cfg.R2AccessKey == "" || cfg.R2BucketName == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
static := []string{
|
||||
"data/meta/archetypes.json",
|
||||
"data/evolution/community_hints.json",
|
||||
}
|
||||
|
||||
for _, rel := range static {
|
||||
localPath := fmt.Sprintf("%s/%s", outputDir, rel)
|
||||
if err := uploadFileToR2(ctx, cfg, localPath, rel); err != nil {
|
||||
slog.Error("Failed to upload meta JSON to R2", "path", rel, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Upload per-match feedback files for matches that have annotations.
|
||||
matchIDs := make(map[string]bool)
|
||||
for _, f := range data.Feedback {
|
||||
matchIDs[f.MatchID] = true
|
||||
}
|
||||
for matchID := range matchIDs {
|
||||
rel := fmt.Sprintf("data/matches/%s/feedback.json", matchID)
|
||||
localPath := fmt.Sprintf("%s/%s", outputDir, rel)
|
||||
if err := uploadFileToR2(ctx, cfg, localPath, rel); err != nil {
|
||||
slog.Error("Failed to upload match feedback to R2", "match_id", matchID, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
slog.Info("Uploaded meta JSON to R2",
|
||||
"static_files", len(static),
|
||||
"match_feedback_files", len(matchIDs),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// runBuildCycle executes one full index build cycle
|
||||
func runBuildCycle(ctx context.Context, db *sql.DB, cfg *Config) error {
|
||||
// Create data directories
|
||||
|
|
|
|||
|
|
@ -53,23 +53,40 @@ export function loadLocalAnnotations(matchId?: string): Annotation[] {
|
|||
|
||||
// ─── Fetch feedback from API ─────────────────────────────────────────────────
|
||||
|
||||
type FeedbackAPIEntry = { feedback_id: string; match_id: string; turn: number; type: FeedbackType; body: string; author: string; upvotes: number; created_at: string };
|
||||
|
||||
function mapFeedbackEntries(entries: FeedbackAPIEntry[]): Annotation[] {
|
||||
return entries.map(f => ({
|
||||
id: f.feedback_id,
|
||||
match_id: f.match_id,
|
||||
turn: f.turn,
|
||||
type: f.type,
|
||||
body: f.body,
|
||||
author: f.author,
|
||||
upvotes: f.upvotes,
|
||||
created_at: f.created_at,
|
||||
}));
|
||||
}
|
||||
|
||||
export async function fetchFeedback(matchId: string): Promise<Annotation[]> {
|
||||
// Try live API first
|
||||
try {
|
||||
const resp = await fetch(`${API_BASE}/feedback/${matchId}`);
|
||||
if (resp.ok) {
|
||||
const data = await resp.json();
|
||||
if (data.feedback && Array.isArray(data.feedback)) {
|
||||
return mapFeedbackEntries(data.feedback as FeedbackAPIEntry[]);
|
||||
}
|
||||
}
|
||||
} catch { /* fall through to static file */ }
|
||||
|
||||
// Fallback: load from pre-built static index (data/matches/{id}/feedback.json)
|
||||
try {
|
||||
const resp = await fetch(`/data/matches/${matchId}/feedback.json`);
|
||||
if (!resp.ok) return [];
|
||||
const data = await resp.json();
|
||||
// API returns { match_id, feedback: FeedbackEntry[] } — map to Annotation
|
||||
if (!data.feedback || !Array.isArray(data.feedback)) return [];
|
||||
return data.feedback.map((f: { feedback_id: string; match_id: string; turn: number; type: FeedbackType; body: string; author: string; upvotes: number; created_at: string }) => ({
|
||||
id: f.feedback_id,
|
||||
match_id: f.match_id,
|
||||
turn: f.turn,
|
||||
type: f.type,
|
||||
body: f.body,
|
||||
author: f.author,
|
||||
upvotes: f.upvotes,
|
||||
created_at: f.created_at,
|
||||
}));
|
||||
return mapFeedbackEntries(data.feedback as FeedbackAPIEntry[]);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue