`yield` inside a recursive procedure

This is a case where it might be easier to take a step back and start fresh.

Let's start by getting the keys and the intervals separate, using a well-known trick involving zip:

>>> keys, intervals = list(zip(*conditions))
>>> keys
('i', 'j')
>>> intervals
((1, 5), (1, 2))

(The correspondence between the two preserves the original pairing; intervals[i] is the interval for the variable keys[i] for all i.)

Now, let's create proper range objects from those intervals

>>> intervals = [range(x, y+1) for x, y in intervals]
>>> list(intervals[0])
[1, 2, 3, 4, 5]
>>> list(intervals[1])
[1, 2]

We can compute the product of these range objects

>>> for v in product(*intervals):
...   print(v)
...
(1, 1)
(1, 2)
(2, 1)
(2, 2)
(3, 1)
(3, 2)
(4, 1)
(4, 2)
(5, 1)
(5, 2)

which you should recognize as the values to use for each dict. You can zip each of those values with the keys to create an appropriate set of arguments for the dict command. For example:

>>> dict(zip(keys, (1,1)))
{'i': 1, 'j': 1}

Putting this all together, we can iterate over the product to produce each dict in turn, yielding it.

def iteration(conditions):
    keys, intervals = zip(*conditions)
    intervals = [range(x, y+1) for x, y in intervals]
    yield from (dict(zip(keys, v)) for v in product(*intervals))