diff --git a/mixtape/core/static/mixtape/core/insights/clusteringTimeline.js b/mixtape/core/static/mixtape/core/insights/clusteringTimeline.js index 4a926b3..761c7a1 100644 --- a/mixtape/core/static/mixtape/core/insights/clusteringTimeline.js +++ b/mixtape/core/static/mixtape/core/insights/clusteringTimeline.js @@ -59,7 +59,6 @@ document.addEventListener('alpine:init', () => { const step = this.$store.insights.currentStep; const xLine = [step, step]; const yLine = [yMin - 0.5, yMax + 0.5]; - // Use restyle: arguments must be arrays-of-arrays per selected trace Plotly.restyle(this.$el, { x: [xLine], y: [yLine] }, [1]); } }); @@ -99,7 +98,7 @@ document.addEventListener('alpine:init', () => { : 1; this.data = [ { - agentStepClusters: agentStepClusters, + z: agentStepClusters, type: 'heatmap', showscale: false, colorscale: 'YlGnBu', diff --git a/mixtape/core/static/mixtape/core/insights/insightsStore.js b/mixtape/core/static/mixtape/core/insights/insightsStore.js index 01cb858..9e136ce 100644 --- a/mixtape/core/static/mixtape/core/insights/insightsStore.js +++ b/mixtape/core/static/mixtape/core/insights/insightsStore.js @@ -21,6 +21,43 @@ document.addEventListener('alpine:init', () => { // Runtime values currentStep: 0, + // Get active timeline key steps based on global/episode mode + getActiveTimelineKeySteps() { + const settings = Alpine.store('settings'); + if (!settings) return []; + const useGlobal = settings.useGlobalTimeline; + const epIdx = settings.timelineEpisodeIdx; + + if (useGlobal && Array.isArray(this.timelineKeySteps)) { + // Global mode: merge all episodes' key steps, sort by reward, take top 40 + const allSteps = []; + this.timelineKeySteps.forEach((episodeSteps, episodeIdx) => { + if (Array.isArray(episodeSteps)) { + episodeSteps.forEach(step => { + allSteps.push({ + ...step, + episodeIdx: episodeIdx, + episodeId: this.episodeIds?.[episodeIdx] || episodeIdx, + }); + }); + } + }); + // Sort by total_rewards descending, take top 40, then sort by step number + const topSteps = allSteps + .sort((a, b) => b.total_rewards - a.total_rewards) + .slice(0, 40) + .sort((a, b) => { + // Sort by episode first, then by step number within episode + if (a.episodeIdx !== b.episodeIdx) return a.episodeIdx - b.episodeIdx; + return a.number - b.number; + }); + return topSteps; + } else { + // Episode mode: return selected episode's key steps + return this.timelineKeySteps?.[epIdx] || []; + } + }, + init() { // Parse JSON data this.parsedData = JSON.parse( diff --git a/mixtape/core/static/mixtape/core/insights/replay.js b/mixtape/core/static/mixtape/core/insights/replay.js index 115956a..dce41b2 100644 --- a/mixtape/core/static/mixtape/core/insights/replay.js +++ b/mixtape/core/static/mixtape/core/insights/replay.js @@ -1,7 +1,14 @@ document.addEventListener('alpine:init', () => { Alpine.data('replay', () => ({ playing: false, + stepInput: 1, maxSteps: Alpine.store('insights').parsedData.max_steps - 1, + init() { + this.stepInput = this.$store.insights.currentStep + 1; + this.$watch('$store.insights.currentStep', (val) => { + this.stepInput = val + 1; + }); + }, stepForward() { if (!this.playing) { return; @@ -14,8 +21,12 @@ document.addEventListener('alpine:init', () => { }, delay); } else { this.playing = false; - this.$store.insights.currentStep = 0; } }, + jumpToStep() { + const val = Math.max(1, Math.min(this.maxSteps + 1, this.stepInput || 1)); + this.stepInput = val; + this.$store.insights.currentStep = val - 1; + }, })); }); diff --git a/mixtape/core/templates/core/insights/episode_info.html b/mixtape/core/templates/core/insights/episode_info.html index 8bac15c..2f40786 100644 --- a/mixtape/core/templates/core/insights/episode_info.html +++ b/mixtape/core/templates/core/insights/episode_info.html @@ -1,41 +1,65 @@ -
-
- - - - - - - - - - - -
Agents