If you handle input directly from the OS's event callback (or similar platform events), you generally only have access to the data the OS gave you for that event. That data usually only pertains to the immediate input action (pressing this key or moving the mouse on that axis, and maybe some information about what modifiers were down). This can make it difficult to properly translate to handling your game's input if your game has more complicated inter-relationships with other input state.
If you instead do you input handling in your main update loop body via cached input state, you have access to all of that information. You may also more easily have access to information that lets you determine if a key is "still down" in a useful way (OS input events may be biased by key repeat delay).
Frankly, I think you should support both. Certain types of input lend themselves to being more easily handling with the first option, others with the second. Possibly even within the same game.
You can implement the second option in terms of the first; listen to input events from the OS or platform and in response, cache that somewhere for your main update loop to handle later. You can also implement a method of letting you game register callback hooks that you fire in response to those platform events, after you have cached the appropriate state.
That way, your game code can handle the input in whichever way makes the most sense for it.