To solve the given optimization problem, we first need to convert the natural language description into a symbolic representation. This involves defining variables, an objective function, and constraints based on the problem statement.

Let's define:
- $x_1$ as the tons of chocolate syrup made per week.
- $x_2$ as the tons of caramel syrup made per week.

The objective is to maximize profit. Given that the profit per ton of chocolate syrup is $500 and the profit per ton of caramel syrup is $350, the objective function can be written as:
\[ \text{Maximize:} \quad 500x_1 + 350x_2 \]

Now, let's consider the constraints:
1. The factory can make at most 15 tons of each syrup per week:
   - $x_1 \leq 15$
   - $x_2 \leq 15$

2. They must make at least 2 tons of chocolate syrup and at least 3 tons of caramel syrup per week:
   - $x_1 \geq 2$
   - $x_2 \geq 3$

3. Each ton of syrup requires 3 hours on the heating machine, and the machine is available for at most 50 hours per week:
   - $3x_1 + 3x_2 \leq 50$

Thus, our symbolic representation in JSON format is:

```json
{
    'sym_variables': [('x1', 'tons of chocolate syrup'), ('x2', 'tons of caramel syrup')],
    'objective_function': '500*x1 + 350*x2',
    'constraints': [
        'x1 <= 15',
        'x2 <= 15',
        'x1 >= 2',
        'x2 >= 3',
        '3*x1 + 3*x2 <= 50'
    ]
}
```

To solve this problem using Gurobi in Python, we'll write the following code:

```python
from gurobipy import *

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

# Define variables
x1 = m.addVar(lb=2, ub=15, vtype=GRB.CONTINUOUS, name="chocolate_syrup")
x2 = m.addVar(lb=3, ub=15, vtype=GRB.CONTINUOUS, name="caramel_syrup")

# Set objective function
m.setObjective(500*x1 + 350*x2, GRB.MAXIMIZE)

# Add constraints
m.addConstr(x1 <= 15, "chocolate_limit")
m.addConstr(x2 <= 15, "caramel_limit")
m.addConstr(x1 >= 2, "min_chocolate")
m.addConstr(x2 >= 3, "min_caramel")
m.addConstr(3*x1 + 3*x2 <= 50, "heating_machine_limit")

# Optimize model
m.optimize()

# Print solution
if m.status == GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f"Chocolate syrup: {x1.x} tons")
    print(f"Caramel syrup: {x2.x} tons")
    print(f"Objective function value: {m.objVal}")
else:
    print("No optimal solution found.")
```