๐Ÿ’ป Exercise: Exercise: Parse a ReAct trace

๐Ÿ“ Instructions

Write parse_react_trace(text) returning a list of step dicts. A step may have keys thought, action, action_input, observation. Actions look like 'Action: name[input]'. An 'Observation:' line closes the current step; a new 'Thought:' also starts a new step if the current one already has content. Standard library only.

๐Ÿงช Initial Code / Tests

๐Ÿ“„ evaluate.py
from unittest import TestCase
from exercise import parse_react_trace


class Evaluate(TestCase):
    def test_parses_action_name(self):
        steps = parse_react_trace('Thought: add them\nAction: add[2, 3]\nObservation: 5')
        self.assertEqual(steps[0]['action'], 'add')

    def test_parses_action_input(self):
        steps = parse_react_trace('Action: add[2, 3]\nObservation: 5')
        self.assertEqual(steps[0]['action_input'], '2, 3')

    def test_observation_closes_step(self):
        steps = parse_react_trace('Thought: a\nAction: x[1]\nObservation: ok')
        self.assertEqual(steps[0]['observation'], 'ok')

    def test_two_steps(self):
        text = 'Thought: a\nAction: x[1]\nObservation: o1\nThought: b\nAction: y[2]\nObservation: o2'
        self.assertEqual(len(parse_react_trace(text)), 2)

โœ… Solutions

๐Ÿ“„ exercise.py
import re

_THOUGHT = re.compile(r'Thought:\s*(.+)')
_ACTION = re.compile(r'Action:\s*([A-Za-z0-9_\-]+)\s*\[(.*?)\]')
_OBS = re.compile(r'Observation:\s*(.+)')


def parse_react_trace(text):
    steps = []
    cur = {}
    for line in text.splitlines():
        line = line.strip()
        if not line:
            continue
        m = _THOUGHT.match(line)
        if m:
            if cur.get('thought') or cur.get('action'):
                steps.append(cur); cur = {}
            cur['thought'] = m.group(1).strip(); continue
        m = _ACTION.match(line)
        if m:
            cur['action'] = m.group(1).strip()
            cur['action_input'] = m.group(2).strip(); continue
        m = _OBS.match(line)
        if m:
            cur['observation'] = m.group(1).strip()
            steps.append(cur); cur = {}
    if cur.get('thought') or cur.get('action'):
        steps.append(cur)
    return steps