To solve this optimization problem, we need to define the decision variables, objective function, and constraints.

Let's denote:
- $x$ as the number of peanut butter smoothies made,
- $y$ as the number of almond butter smoothies made.

The profit per peanut butter smoothie is $3, and the profit per almond butter smoothie is $5. Therefore, the total profit can be represented by the objective function: $3x + 5y$.

We have three types of constraints based on the availability of ingredients:
1. Peanut butter constraint: $2x \leq 80$, because each peanut butter smoothie requires 2 units of peanut butter and there are 80 units available.
2. Almond butter constraint: $3y \leq 90$, since each almond butter smoothie requires 3 units of almond butter and there are 90 units available.
3. Milk constraint: $3x + 3y \leq 100$, as both types of smoothies require 3 units of milk per smoothie, and there are 100 units of milk available.

Non-negativity constraints also apply since the number of smoothies cannot be negative:
- $x \geq 0$
- $y \geq 0$

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

```python
from gurobipy import *

# Create a new model
m = Model("Smoothie_Optimization")

# Define the decision variables
x = m.addVar(vtype=GRB.CONTINUOUS, name="peanut_butter_smoothies", lb=0)
y = m.addVar(vtype=GRB.CONTINUOUS, name="almond_butter_smoothies", lb=0)

# Set the objective function
m.setObjective(3*x + 5*y, GRB.MAXIMIZE)

# Add constraints
m.addConstr(2*x <= 80, "peanut_butter_constraint")
m.addConstr(3*y <= 90, "almond_butter_constraint")
m.addConstr(3*x + 3*y <= 100, "milk_constraint")

# Optimize the model
m.optimize()

# Print the results
if m.status == GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f"Peanut butter smoothies: {x.x}")
    print(f"Almond butter smoothies: {y.x}")
    print(f"Total profit: ${3*x.x + 5*y.x:.2f}")
else:
    print("No optimal solution found.")
```