However, if your fear with CSG is that it would be unoptimized – in my experience as long as you aren’t constantly updating the mesh, it is actually pretty optimized.
Hopefully this helps. Sorry I don’t have a better solution.
It has also just occurred to me:
You could do a different shape for the flask and use scaling.
For example, if you modeled the flask in this shape:
You could just use scaling of the part inside.
This is sort of an awful solution, but I should mention it because it would certainly work and look good.
You can make an empty flask in Blender, by making the flask model, grouping the parts together, and then duplicating the final model (that is filled). With the duplicate, you can position it inside of the full flask model in a way that you can “empty” the filled area of the full flask through a modifier. If you duplicate the smaller flask before modifying the larger flask (and therefore getting rid of the model of the smaller flask), you can break it into smaller pieces in Blender (using the same modifier), which can be used to “fill” the flask to different heights (through scripting). In Roblox Studio, you can make multiple Unions with these sections of the flask’s liquid, which can be scripted to appear with a script (depending on how full you want the bottle to appear)…this would also allow for a filling animation.
Note: I forgot the name of the modifier that you can use to “empty” the flask.