Skip to content

Commit 7c43db6

Browse files
committed
Make sure visualizer src/lib is included
1 parent 8a3a661 commit 7c43db6

4 files changed

Lines changed: 260 additions & 0 deletions

File tree

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ eggs/
2323
lib/
2424
lib64/
2525
!docs/src/lib/
26+
!visualizer/src/lib/
2627
parts/
2728
sdist/
2829
var/

‎visualizer/src/lib/parse-logs.ts‎

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import { RLMIteration, RLMLogFile, LogMetadata, RLMConfigMetadata, extractFinalAnswer } from './types';
2+
3+
// Extract the context variable from code block locals
4+
export function extractContextVariable(iterations: RLMIteration[]): string | null {
5+
for (const iter of iterations) {
6+
for (const block of iter.code_blocks) {
7+
if (block.result?.locals?.context) {
8+
const ctx = block.result.locals.context;
9+
if (typeof ctx === 'string') {
10+
return ctx;
11+
}
12+
}
13+
}
14+
}
15+
return null;
16+
}
17+
18+
// Default config when metadata is not present (backwards compatibility)
19+
function getDefaultConfig(): RLMConfigMetadata {
20+
return {
21+
root_model: null,
22+
max_depth: null,
23+
max_iterations: null,
24+
backend: null,
25+
backend_kwargs: null,
26+
environment_type: null,
27+
environment_kwargs: null,
28+
other_backends: null,
29+
};
30+
}
31+
32+
export interface ParsedJSONL {
33+
iterations: RLMIteration[];
34+
config: RLMConfigMetadata;
35+
}
36+
37+
export function parseJSONL(content: string): ParsedJSONL {
38+
const lines = content.trim().split('\n').filter(line => line.trim());
39+
const iterations: RLMIteration[] = [];
40+
let config: RLMConfigMetadata = getDefaultConfig();
41+
42+
for (const line of lines) {
43+
try {
44+
const parsed = JSON.parse(line);
45+
46+
// Check if this is a metadata entry
47+
if (parsed.type === 'metadata') {
48+
config = {
49+
root_model: parsed.root_model ?? null,
50+
max_depth: parsed.max_depth ?? null,
51+
max_iterations: parsed.max_iterations ?? null,
52+
backend: parsed.backend ?? null,
53+
backend_kwargs: parsed.backend_kwargs ?? null,
54+
environment_type: parsed.environment_type ?? null,
55+
environment_kwargs: parsed.environment_kwargs ?? null,
56+
other_backends: parsed.other_backends ?? null,
57+
};
58+
} else {
59+
// This is an iteration entry
60+
iterations.push(parsed as RLMIteration);
61+
}
62+
} catch (e) {
63+
console.error('Failed to parse line:', line, e);
64+
}
65+
}
66+
67+
return { iterations, config };
68+
}
69+
70+
export function extractContextQuestion(iterations: RLMIteration[]): string {
71+
if (iterations.length === 0) return 'No context found';
72+
73+
const firstIteration = iterations[0];
74+
const prompt = firstIteration.prompt;
75+
76+
// Look for user message that contains the actual question
77+
for (const msg of prompt) {
78+
if (msg.role === 'user' && msg.content) {
79+
// Try to extract quoted query
80+
const queryMatch = msg.content.match(/original query: "([^"]+)"/);
81+
if (queryMatch) {
82+
return queryMatch[1];
83+
}
84+
85+
// Check if it contains the actual query pattern
86+
if (msg.content.includes('answer the prompt')) {
87+
continue;
88+
}
89+
90+
// Take first substantial user message
91+
if (msg.content.length > 50 && msg.content.length < 500) {
92+
return msg.content.slice(0, 200) + (msg.content.length > 200 ? '...' : '');
93+
}
94+
}
95+
}
96+
97+
// Fallback: look in system prompt for context info
98+
const systemMsg = prompt.find(m => m.role === 'system');
99+
if (systemMsg?.content) {
100+
const contextMatch = systemMsg.content.match(/context variable.*?:(.*?)(?:\n|$)/i);
101+
if (contextMatch) {
102+
return contextMatch[1].trim().slice(0, 200);
103+
}
104+
}
105+
106+
// Check code block output for actual context
107+
for (const iter of iterations) {
108+
for (const block of iter.code_blocks) {
109+
if (block.result?.locals?.context) {
110+
const ctx = block.result.locals.context;
111+
if (typeof ctx === 'string' && ctx.length < 500) {
112+
return ctx;
113+
}
114+
}
115+
}
116+
}
117+
118+
return 'Context available in REPL environment';
119+
}
120+
121+
export function computeMetadata(iterations: RLMIteration[]): LogMetadata {
122+
let totalCodeBlocks = 0;
123+
let totalSubLMCalls = 0;
124+
let totalExecutionTime = 0;
125+
let hasErrors = false;
126+
let finalAnswer: string | null = null;
127+
128+
for (const iter of iterations) {
129+
totalCodeBlocks += iter.code_blocks.length;
130+
131+
// Use iteration_time if available, otherwise sum code block times
132+
if (iter.iteration_time != null) {
133+
totalExecutionTime += iter.iteration_time;
134+
} else {
135+
for (const block of iter.code_blocks) {
136+
if (block.result) {
137+
totalExecutionTime += block.result.execution_time || 0;
138+
}
139+
}
140+
}
141+
142+
for (const block of iter.code_blocks) {
143+
if (block.result) {
144+
if (block.result.stderr) {
145+
hasErrors = true;
146+
}
147+
if (block.result.rlm_calls) {
148+
totalSubLMCalls += block.result.rlm_calls.length;
149+
}
150+
}
151+
}
152+
153+
if (iter.final_answer) {
154+
finalAnswer = extractFinalAnswer(iter.final_answer);
155+
}
156+
}
157+
158+
return {
159+
totalIterations: iterations.length,
160+
totalCodeBlocks,
161+
totalSubLMCalls,
162+
contextQuestion: extractContextQuestion(iterations),
163+
finalAnswer,
164+
totalExecutionTime,
165+
hasErrors,
166+
};
167+
}
168+
169+
export function parseLogFile(fileName: string, content: string): RLMLogFile {
170+
const { iterations, config } = parseJSONL(content);
171+
const metadata = computeMetadata(iterations);
172+
173+
return {
174+
fileName,
175+
filePath: fileName,
176+
iterations,
177+
metadata,
178+
config,
179+
};
180+
}
181+

‎visualizer/src/lib/types.ts‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Types matching the RLM log format
2+
3+
export interface RLMChatCompletion {
4+
prompt: string | Record<string, unknown>;
5+
response: string;
6+
prompt_tokens: number;
7+
completion_tokens: number;
8+
execution_time: number;
9+
}
10+
11+
export interface REPLResult {
12+
stdout: string;
13+
stderr: string;
14+
locals: Record<string, unknown>;
15+
execution_time: number;
16+
rlm_calls: RLMChatCompletion[];
17+
}
18+
19+
export interface CodeBlock {
20+
code: string;
21+
result: REPLResult;
22+
}
23+
24+
export interface RLMIteration {
25+
type?: string;
26+
iteration: number;
27+
timestamp: string;
28+
prompt: Array<{ role: string; content: string }>;
29+
response: string;
30+
code_blocks: CodeBlock[];
31+
final_answer: string | [string, string] | null;
32+
iteration_time: number | null;
33+
}
34+
35+
// Metadata saved at the start of a log file about RLM configuration
36+
export interface RLMConfigMetadata {
37+
root_model: string | null;
38+
max_depth: number | null;
39+
max_iterations: number | null;
40+
backend: string | null;
41+
backend_kwargs: Record<string, unknown> | null;
42+
environment_type: string | null;
43+
environment_kwargs: Record<string, unknown> | null;
44+
other_backends: string[] | null;
45+
}
46+
47+
export interface RLMLogFile {
48+
fileName: string;
49+
filePath: string;
50+
iterations: RLMIteration[];
51+
metadata: LogMetadata;
52+
config: RLMConfigMetadata;
53+
}
54+
55+
export interface LogMetadata {
56+
totalIterations: number;
57+
totalCodeBlocks: number;
58+
totalSubLMCalls: number;
59+
contextQuestion: string;
60+
finalAnswer: string | null;
61+
totalExecutionTime: number;
62+
hasErrors: boolean;
63+
}
64+
65+
export function extractFinalAnswer(answer: string | [string, string] | null): string | null {
66+
if (!answer) return null;
67+
if (Array.isArray(answer)) {
68+
return answer[1];
69+
}
70+
return answer;
71+
}
72+

‎visualizer/src/lib/utils.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { clsx, type ClassValue } from "clsx"
2+
import { twMerge } from "tailwind-merge"
3+
4+
export function cn(...inputs: ClassValue[]) {
5+
return twMerge(clsx(inputs))
6+
}

0 commit comments

Comments
 (0)