Project Euler 92: sum of squares of digits until a loop is encountered
Prefer integer arithmetic

def get_digits(num):
    while n:
        yield n % 10
        n //= 10


Integer arithmetic is way faster than string convertion and manipulation, so this alternate version improves the efficiency of the code.

Give names appreciating the generality of your functions

sum_sq_digits works on any integer list. It works on any integer sequence, not just digits, but your name hints that it only works on a list of digits. I would rename it: sum_squared

Make a final function

I like that you wrote many small functions, it really helps readibility, I suggest going the last step and writing a function to solve the problem:

def chain_ending(limit, target)
   for digits in islice(
      combinations_with_replacement(range(10), limit),
      1,
      None):

      if sum_sq_chain(sum_sq_digits(digits)) == target:
          cur = fact(limit)
          for _, v in groupby(digits):
              cur //= fact(len(list(v)))
          yield curr


This way testing it is simpler and you may reuse it with different parameters. To get the final answer, just sum the generator this function yields.

Re-use

You will want to reuse as much as possible of your logic, to avoid duplicating work and possible errors.

The factorial function makes use of an in-place-defined product, I think that a separate product function will make code a little more English-like and will save you effort from re-writing it as getting the product of a sequence is a common task.

By the same point of view, using if __name__ == "__main__" allows you to import the script without running it, enabling re-use.

Comments for non-obvious algoritmhs

You do not use plain bruteforce to solve the problem, that is good, but you should leave a comment whenever you use a non-obvious algoritmh, or a link to an explanation of it.

Good

Overall this is good, clear, modular code.