In a query
function, how do I determine whether it was called as a query or as an update call?
There is no way to detect this. But even if you call a query function in replicated mode (what you refer to as ‘as an update call’) the state changes will be discarded at the end. If you truly want to have both options, then you have to define two separate functions on your canister