From OSM perspective, it’s going to look like 2 left turns at separate nodes. So I’m wondering what information from OSM does graphhopper use to determine if it’s actually a U-turn rather than 2 left turns?
Do you mean how it detects it for the instructions? There it uses a heuristic to detect two close left turns on a way with the same name - see this comment and the following code. If there is a bug or problem - let us know.
Of course there could be also relations in the OSM data that says that this u turn is forbidden. Such “turn restriction” relations with via-ways can become relatively complex and there are a number of issues on Github that explain this in more detail.