To solve this optimization problem, we first need to define the decision variables and the objective function. Let's denote the number of large coffees as \(L\) and the number of small coffees as \(S\). The objective is to maximize profit, which can be calculated based on the profits per coffee type.

The profit per large coffee is $5, and per small coffee is $3. Thus, the total profit \(P\) can be represented as:
\[ P = 5L + 3S \]

We have constraints based on the availability of coffee beans and milk. A large coffee requires 12 units of coffee beans and 20 units of milk, while a small coffee requires 8 units of coffee beans and 15 units of milk. Given that there are 1000 units of coffee beans and 1500 units of milk available, we can write the following constraints:
\[ 12L + 8S \leq 1000 \] (coffee beans constraint)
\[ 20L + 15S \leq 1500 \] (milk constraint)

Additionally, \(L\) and \(S\) must be non-negative since they represent quantities of coffees.

Now, let's translate this problem into Gurobi code in Python:

```python
from gurobipy import *

# Create a new model
model = Model("Coffee Shop Optimization")

# Define the decision variables
L = model.addVar(vtype=GRB.CONTINUOUS, name="Large_Coffees", lb=0)
S = model.addVar(vtype=GRB.CONTINUOUS, name="Small_Coffees", lb=0)

# Set the objective function
model.setObjective(5*L + 3*S, GRB.MAXIMIZE)

# Add constraints
model.addConstr(12*L + 8*S <= 1000, "Coffee_Beans_Constraint")
model.addConstr(20*L + 15*S <= 1500, "Milk_Constraint")

# Optimize the model
model.optimize()

# Print the results
if model.status == GRB.OPTIMAL:
    print(f"Optimal solution: L = {L.x}, S = {S.x}")
    print(f"Maximum profit: ${5*L.x + 3*S.x:.2f}")
else:
    print("No optimal solution found")
```