What you want is not possible with any built-in facility in Python shy of eval; f-strings are a syntactic feature of the language itself, there is no built-in function or module that implements them in a way you can use aside from eval. In CPython, when the compiler gets through with an f-string, there is nothing left of the original "string", it's been broken down into bytecode constructing the individual elements, followed by an instruction that puts them all together (omitted for simple cases where there is only one component being built). For example:
import dis
dis.dis("f'{a.bit_length()}{len(b)}'")
produces:
1 0 LOAD_NAME 0 (a)
2 LOAD_METHOD 1 (bit_length)
4 CALL_METHOD 0
6 FORMAT_VALUE 0
8 LOAD_NAME 2 (len)
10 LOAD_NAME 3 (b)
12 CALL_FUNCTION 1
14 FORMAT_VALUE 0
16 BUILD_STRING 2
18 RETURN_VALUE
Try it online!
(the _NAME bytecodes might be _FAST or _GLOBAL or the like when running in functions, and the RETURN_VALUE is an artifact of how dis works with isolated expressions, but the general structure is the otherwise the same).
If you need essentially arbitrary code execution from provided template text (that's what f-strings are after all), you're stuck going with eval or a third-party library with similar (not identical) functionality, e.g. Jinja2 and the like. In practice, I think you have an XY problem, but on the off chance you don't, you want third party full-featured templating.