<reasoning_content>
Thinking Process:

1.  **Analyze the Request:**
    *   Goal: Extend a job queue with retry logic and exponential backoff.
    *   Specific Requirements:
        1.  Add `process_job(job_id: str, processor: Callable) -> bool`.
        2.  Retry up to 3 times on exception.
        3.  Exponential backoff (1s, 2s, 4s).
        4.  Track retry count per job.
        5.  Return `True` on success, `False` on exhaustion.
    *   Constraint: Write *only* Python code, no explanations.
    *   Context: Implicitly, there's an existing `JobQueue` class (implied by the example usage `queue = JobQueue()`), but I need to provide the implementation that fits the requirements. The prompt asks to "Extend the job queue", implying I should define the class or methods necessary. Since no existing code is provided, I should define a minimal `JobQueue` class that supports `add_job` and the new `process_job` method to make the example valid.
    *   Backoff handling: Delays can be stored/tracked rather than actually sleeping (simulated).

2.  **Design the `JobQueue` Class:**
    *   Needs storage for jobs. A dictionary or queue structure seems appropriate.
    *   Needs to track retry counts per job.
    *   Needs to handle the `process_job` logic.

3.  **Implement `process_job`:**
    *   Signature: `process_job(self, job_id: str, processor: Callable) -> bool`.
    *   Logic:
        *   Check if job exists (or create if needed, but `add_job` is mentioned in the example).
        *   Loop for retries (max 3 times).
        *   Try calling `processor`.
        *   If success, return `True`.
        *   If exception, increment retry count, calculate backoff, store backoff delay.
        *   If retries exhausted, return `False`.

4.  **Backoff Logic:**
    *   Sequence: 1, 2, 4.
    *   Formula: `2^(attempt-1)` for attempt starting at 1?
    *   Attempt 1 (failure): Wait 1s.
    *   Attempt 2 (failure): Wait 2s.
    *   Attempt 3 (failure): Wait 4s.
    *   Wait 4 times total? No, "retry up to 3 times". Usually means 1 initial + 3 retries = 4 attempts, or 1 attempt + 3 retries = 4 total tries.
    *   Interpretation: "retry up to 3 times" usually means if it fails the first time, try again (1), fail again, try again (2), fail again, try again (3). Total attempts = 1 + 3 = 4.
    *   Wait times: 1s, 2s, 4s.
    *   Let's align with the prompt's specific sequence: "1s, 2s, 4s". This implies 3 retry intervals.
    *   So:
        *   Attempt 1 (Initial): Execute.
        *   Fail -> Retry 1 (Wait 1s).
        *   Fail -> Retry 2 (Wait 2s).
        *   Fail -> Retry 3 (Wait 4s).
        *   Fail -> Return False.
    *   Tracking: Store the backoff delay in the job metadata.

5.  **Drafting the Code:**

    ```python
    import time
    from typing import Callable, Any, Dict, Optional

    class JobQueue:
        def __init__(self):
            self.jobs: Dict[str, Any] = {}
            self.retry_counts: Dict[str, int] = {}
            self.backoff_delays: Dict[str, float] = {}

        def add_job(self, job_id: str, data: Any):
            self.jobs[job_id] = data
            self.retry_counts[job_id] = 0
            self.backoff_delays[job_id] = 0

        def process_job(self, job_id: str, processor: Callable) -> bool:
            if job_id not in self.jobs:
                return False

            max_retries = 3
            base_backoff = 1
            attempts = 0

            while attempts <= max_retries:
                try:
                    processor(self.jobs[job_id])
                    return True
                except Exception:
                    self.retry_counts[job_id] += 1
                    attempts += 1
                    if attempts <= max_retries:
                        # Calculate backoff: 2^(attempts-1) -> 1, 2, 4
                        delay = base_backoff * (2 ** (attempts - 1))
                        self.backoff_delays[job_id] = delay
                    else:
                        self.backoff_delays[job_id] = 0 # No wait after last fail
            return False
    ```

    Refining based on "retry up to 3 times".
    If I call `process_job`, and it fails immediately:
    1. Fail.
    2. Retry 1 (Wait 1s).
    3. Fail.
    4. Retry 2 (Wait 2s).
    5. Fail.
    6. Retry 3 (Wait 4s).
    7. Fail.
    8. Stop.

    So loop range should be `range(max_retries + 1)`.
    Wait, the requirement says "Use exponential backoff between retries: 1s, 2s, 4s".
    This implies 3 wait periods.
    So:
    - Attempt 1 (Initial)
    - Wait 1s
    - Attempt 2
    - Wait 2s
    - Attempt 3
    - Wait 4s
    - Attempt 4
    - Fail

    This is 3 retries (1, 2, 4).
    Code logic:
    ```python
    max_retries = 3
    for i in range(max_retries + 1): # 0, 1, 2, 3 (4 attempts)
        try:
            return processor(...)
        except:
            if i < max_retries:
                delay = 2 ** i # 1, 2, 4
                self.backoff_delays[job_id] = delay
            else:
                return False
    ```
    Wait, if `i` is 0 (first attempt), fail. `i < 3`. `delay = 2**0 = 1`.
    If `i` is 1 (second attempt), fail. `i < 3`. `delay = 2**1 = 2`.
    If `i` is 2 (third attempt), fail. `i < 3`. `delay = 2**2 = 4`.
    If `i` is 3 (fourth attempt), fail. `i == 3`. Return False.

    This matches the 1s, 2s, 4s sequence for the 3 retries.

6.  **Reviewing Constraints:**
    *   "Write only the Python code, no explanations."
    *   Need to make sure imports are included if necessary (standard lib mostly).
    *   Need to ensure `add_job` exists based on the example provided in the prompt (`queue.add_job(...)`).
    *   Need to ensure `process_job` signature matches.
    *   Need to ensure retry count tracking is explicit.
    *   Need to ensure backoff delays are stored.

7.  **Final Code Structure:**
    *   Import `Callable` from typing.
    *   Define `JobQueue`.
    *   Define `add_job`.
    *   Define `process_job`.
    *   Implement logic inside `process_job`.

    One detail: "Track retry count for each job".
    I should store `retry_counts[job_id]`.

    Another detail: "The backoff delays can be stored/tracked rather than actually sleeping."
    I will update `self.backoff_delays[job_id]`.

    Let's write the code.

```python
from typing import Callable, Dict, Any

class JobQueue:
    def __init__(self):
        self.jobs: Dict[str, Any] = {}
        self.retry_counts: Dict[str, int] = {}
        self.backoff_delays: Dict[str, int] = {}

    def add_job(self, job_id: str, data: Any):
        self.jobs[job_id] = data
        self.retry_counts[job_id] = 0
        self.backoff_delays[job_id] = 0

    def process_job(self, job_id: str, processor: Callable) -> bool:
        if job_id not in self.jobs:
            return False
        
        max_retries = 3
        attempts = 0
        
        while attempts <= max_retries:
            try:
                processor(self.jobs[job_id])
                return True
            except Exception:
                self.retry_counts[job_id] += 1
                attempts += 1
                if attempts <= max_retries:
                    # 1s, 2s, 4s for retries 1, 2, 3
                    delay = 1 * (2 ** (attempts - 1))
                    self.backoff_delays[job_id] = delay
                else:
                    return False
        return False
```
    Wait, the `attempts` logic in the `while` loop needs to be precise.
    If `attempts` starts at 0.
    Iteration 1: `attempts`=0. Fail. `attempts` becomes 1. `attempts <= 3`. Delay = `1 * 2**(0)` = 1.
    Iteration 2: `attempts`=1. Fail. `attempts` becomes 2. `attempts <= 3`. Delay = `1 * 2**(1)` = 2.
    Iteration 3: `attempts`=2. Fail. `attempts` becomes 3. `attempts <= 3`. Delay = `1 * 2**(2)` = 4.
    Iteration 4: `attempts`=3. Fail. `attempts` becomes 4. `attempts <= 3` is False. Return False.

    This logic works for 3 retries (1, 2, 4 delays).
    Wait, if `attempts` becomes 4, I return False immediately inside the except block?
    Yes, `if attempts <= max_retries` checks if we should wait before next attempt.
    If `attempts` is 4 (meaning we just failed the 4th try), we shouldn't wait, we should return False.
    So:
    ```python
    except Exception:
        self.retry_counts[job_id] += 1
        attempts += 1
        if attempts <= max_retries:
            delay = 1 * (2 ** (attempts - 1))
            self.backoff_delays[job_id] = delay
        else:
            return False
    ```
    If `attempts` reaches 4 (after 3 retries + 1 initial):
    `attempts` = 4. `4 <= 3` is False. Else `return False`.
    This seems correct.

    Wait, `attempts` variable usage in `while attempts <= max_retries`.
    If `max_retries` = 3.
    Start `attempts` = 0.
    Loop condition `0 <= 3`.
    Fail. `attempts` -> 1. Delay 1.
    Loop condition `1 <= 3`.
    Fail. `attempts` -> 2. Delay 2.
    Loop condition `2 <= 3`.
    Fail. `attempts` -> 3. Delay 4.
    Loop condition `3 <= 3`.
    Fail. `attempts` -> 4. Else `return False`.
    Loop ends (implicitly because of return).

    Wait, if I return False inside the loop, I exit the loop.
    If I don't return False (success case), I return True.
    If loop finishes (which shouldn't happen if logic is right), return False.

    One check: `process_job` returns `True` if succeeded.
    If I return `True` inside the try block, I am done.
    If I hit the `except` block, I increment retry count.
    The requirement says "Track retry count for each job".
    My code increments `retry_counts[job_id]` on exception.
    If it succeeds on retry, `retry_counts` reflects how many failures occurred before success.
    This seems appropriate.

    Final check on imports and types.
    `from typing import Callable` is needed.
    No explanations.

    One small correction: The prompt example shows `queue.add_job("job1", {"url": "https://example.com"})`.
    My `add_job` takes `data`. That matches.
    The prompt example shows `success = queue.process_job("job1", fetch_url)`.
    My `process_job` takes `job_id` and `processor`. That matches.
    The processor logic in example: `fetch_url(data)`.
    My code calls `processor(self.jobs[job_id])`. That matches.

    Wait, I should make sure I don't use `requests` in the class definition unless I import it, but the example uses `requests`. I should probably not import `requests` inside the class to keep it clean, or just assume the environment has it. Since I need to write the code for the class, I shouldn't rely on external libs like `requests` unless I import them. The example code uses `requests`, but the requirement is to write the `JobQueue` class logic. I will import `requests` just in case the example implies it should be runnable, or just ignore it since it's example usage. Better to stick to standard types. I won't import `requests` inside the class definition code block to avoid dependency assumptions, but I will use `typing`.

    Actually, looking at the prompt "Write only the Python code", I should provide the implementation.

    Let's refine the backoff calculation.
    Requirement: "1s, 2s, 4s".
    My logic: `1 * (2 ** (attempts - 1))`.
    Attempt 1 (after 1st fail): `2**0` = 1.
    Attempt 2 (after 2nd fail): `2**1` = 2.
    Attempt 3 (after 3rd fail): `2**2` = 4.
    Attempt 4 (after 4th fail): Return False.
    This matches.

    Wait, `attempts` variable naming might be confusing. `retry_count` is clearer for the tracking.
    Let's use `retry_count` for the tracking dict and a local counter for the loop.

    Revised logic:
    ```python
    retries_left = max_retries # 3
    while retries_left >= 0: # 0 to 3 (4 attempts)
       try:
          return True
       except:
          self.retry_counts[job_id] += 1
          retries_left -= 1
          if retries_left > 0:
              delay = 1 * (2 ** (max_retries - retries_left))
              self.backoff_delays[job_id] = delay
          else:
              return False
    ```
    Wait, `max_retries` is 3.
    Start: `retries_left` = 3.
    1. Fail. `retries_left` = 2. `delay = 1 * 2^(3-2) = 2`. Wait, requirement says 1s first.
    So `delay` should be `1 * (2 ** (max_retries - retries_left - 1))`?
    Let's stick to the previous logic which was clearer.
    `attempts` starts at 0.
    Loop `while attempts <= max_retries`.
    Inside except:
    `attempts += 1` (now 1).
    `delay = 1 * (2 ** (attempts - 1))` -> `1 * 2**0` = 1.
    This works.

    One detail: "Track retry count for each job".
    I am incrementing `self.retry_counts[job_id]` inside the `except` block.
    If the job succeeds on the 2nd attempt (1 retry), `retry_counts` will be 1.
    If it fails all 4 attempts, `retry_counts` will be 3.
    This seems to track the number of retries (failures) correctly.

    Wait, if I return False immediately in the `except` block when `attempts` > `max_retries`, I exit the loop.
    But the `while` condition checks `attempts <= max_retries`.
    If `attempts` becomes 4, `4 <= 3` is False.
    So I can just let the loop finish and return False at the end.
    However, to store the backoff delay before failing, I need to store it before returning False.
    If I return False at the end, I might not store the last delay?
    The requirement says "Use exponential backoff between retries".
    Usually, you don't wait after the last failure.
    So storing the delay is for the *next* retry.
    So if I fail the last attempt, I don't need to store a delay because I stop.
    But if I fail the 3rd attempt (1st retry), I store 1s.
    If I fail the 4th attempt (3rd retry), I don't store a delay (or 0).
    My code stores delay if `attempts <= max_retries`.
    If `attempts` becomes 4 (after 3 retries), `4 <= 3` is False. No delay stored.
    This is correct behavior (no wait after final failure).
    Wait, if I fail the 3rd attempt (which is the 3rd retry), `attempts` becomes 3. `3 <= 3` is True. Delay stored (4s).
    Then loop continues. Next iteration `attempts` is 3. `while 3 <= 3`.
    Inside loop. Call processor.
    If fails. `attempts` becomes 4. `4 <= 3` False. Return False.
    So the 4s delay is stored for the attempt that follows.
    Wait, the sequence is 1s, 2s, 4s.
    1. Fail (Init). Wait 1s.
    2. Fail (Retry 1). Wait 2s.
    3. Fail (Retry 2). Wait 4s.
    4. Fail (Retry 3). Stop.
    So there are 3 waits.
    My code:
    Init Fail. `attempts`=1. `1 <= 3`. Delay 1.
    Retry 1 Fail. `attempts`=2. `2 <= 3`. Delay 2.
    Retry 2 Fail. `attempts`=3. `3 <= 3`. Delay 4.
    Retry 3 Fail. `attempts`=4. `4 <= 3`. False. Return False.
    This matches the 1s, 2s, 4s sequence for the 3 retries.
    The 4th attempt (Retry 3) happens, if it fails, we stop.
    So `process_job` returns False.
    This logic holds.

    One final check on `add_job`.
    The example shows `queue.add_job("job1", {"url": "https://example.com"})`.
    My `add_job` signature: `add_job(self, job_id: str, data: Any)`.
    This fits.

    Final check on imports.
    I need `Callable` from `typing`.
    I will write the code now.

    Wait, I should check if `retry_counts` needs to be initialized for new jobs.
    Yes, `add_job` does that.

    One edge case: `process_job` called for a job not in queue.
    My code checks `if job_id not in self.jobs: return False`.
    This is safe.

    Code looks solid.

```python
from typing import Callable, Dict, Any

class JobQueue:
    def __init__(self):
        self.jobs: Dict[str, Any] = {}
        self.retry_counts: Dict[str, int] = {}
        self.backoff_delays: Dict[str, int] = {}

    def add_job(self, job_id: str, data: Any):
        self.jobs[job_id] = data
        self.retry_counts[job_id] = 0
        self.backoff_delays[job_id] = 0

    def process_job(self, job_id: str, processor: Callable) -> bool:
        if job_id not in self.jobs:
            return False

        max_retries = 3
        attempts = 0

        while attempts <= max_retries:
            try:
                processor(self.jobs[job_id])
                return True
            except Exception:
                self.retry_counts[job_id] += 1
                attempts += 1
                if attempts <= max_retries:
                    delay = 1 * (2 ** (attempts - 1))
                    self.backoff_delays[job_id] = delay
                else:
                    return False
        return False
```
    Wait, `return False` inside the `except` block when `attempts > max_retries` is redundant if the loop condition handles it, but explicit is better for clarity and to ensure we don't loop infinitely if logic changes.
    Actually, if `attempts` becomes 4, `4 <= 3` is False. The loop terminates.
    Then `return False` at the end of the function executes.
    So the `else: return False` inside the loop is actually unreachable if `attempts` increments to 4.
    Wait.
    Iteration 1: `attempts` 0 -> 1. `1 <= 3`.
    Iteration 2: `attempts` 1 -> 2. `2 <= 3`.
    Iteration 3: `attempts` 2 -> 3. `3 <= 3`.
    Iteration 4: `attempts` 3 -> 4. `4 <= 3` (False).
    So inside Iteration 4:
    `attempts` becomes 4. `if 4 <= 3` is False.
    It does NOT return False inside the if.
    It finishes the except block.
    It goes back to `while`. `4 <= 3` is False.
    Loop exits.
    Function returns `False`.
    So the `else: return False` inside the `except` block is actually redundant if I rely on the loop condition to exit.
    However, to store the backoff delay for the *last* retry before giving up?
    Wait, 1, 2, 4.
    If I fail attempt 1 (init). Wait 1s.
    If I fail attempt 2 (retry 1). Wait 2s.
    If I fail attempt 3 (retry 2). Wait 4s.
    If I fail attempt 4 (retry 3). Stop.
    So I need to store delays for attempts 1, 2, 3.
    My code stores delay if `attempts <= max_retries`.
    If `attempts` is 1, 2, 3. `1<=3`, `2<=3`, `3<=3`. All store.
    If `attempts` is 4. `4<=3`. False. No store.
    This matches the requirement (3 retries = 3 delays).
    So the `else: return False` inside the loop is not needed, but having `return False` at the end is.
    I'll clean it up to be minimal and correct.

    Wait, one more thing. `max_retries` = 3.
    If I set `max_retries = 3`, and loop `while attempts <= max_retries`.
    `attempts` goes 0, 1, 2, 3. (4 iterations).
    1. Init (0). Fail. `attempts` -> 1. Store 1s.
    2. Retry 1 (1). Fail. `attempts` -> 2. Store 2s.
    3. Retry 2 (2). Fail. `attempts` -> 3. Store 4s.
    4. Retry 3 (3). Fail. `attempts` -> 4. No store.
    Loop ends. Return False.
    This matches exactly 3 retries with 1s, 2s, 4s.

    Wait, I should verify the `attempts` logic again.
    Start `attempts = 0`.
    Loop `0 <= 3`.
    Fail. `attempts = 1`. `1 <= 3`. Store `2**(0)` = 1.
    Loop `1 <= 3`.
    Fail. `attempts = 2`. `2 <= 3`. Store `2**(1)` = 2.
    Loop `2 <= 3`.
    Fail. `attempts = 3`. `3 <= 3`. Store `2**(2)` = 4.
    Loop `3 <= 3`.
    Fail. `attempts = 4`. `4 <= 3` False.
    Loop `4 <= 3` False.
    End.
    This works.

    Wait, I'll remove the `else: return False` inside the loop to avoid confusion, as the function returns `False` at the end anyway.
    Actually, I'll keep the explicit return False inside the loop if `attempts` exceeds, just to be explicit about "no more retries". But since the loop condition handles it, I can simplify.
    Wait, if I remove the `else: return False`, and the loop exits, I return `False`.
    If I keep it, I return `False` immediately.
    It's functionally equivalent. I will remove the `else` to keep it cleaner, relying on the loop condition and the final return.
    Wait, if I remove `else: return False`, I need to ensure the loop condition fails correctly.
    Yes, `attempts` increments to 4. `4 <= 3` is False. Loop ends. `return False` at end.
    This is cleaner.

    Wait, I need to make sure I don't return `True` at the end.
    If loop finishes, it means all attempts failed. So `False`.
    If try block succeeds, `return True` happens immediately.
    So `return False` at the end is correct.

    One detail: `from typing import Callable`.
    I will include imports.

    Final check on constraints: "Write only the Python code, no explanations."
    I will output the code block.

</reasoning_content>

<content>
```python
from typing import Callable, Dict, Any

class JobQueue:
    def __init__(self):
        self.jobs: Dict[str, Any] = {}
        self.retry_counts: Dict[str, int] = {}
        self.backoff_delays: Dict[str, int] = {}

    def add_job(self, job_id: str, data: Any):
        self.jobs[job_id] = data
        self.retry_counts[job_id] = 0
        self.backoff_delays[job_id] = 0

    def process_job(self, job_id: str, processor: Callable) -> bool:
        if job_id not in self.jobs:
            return False

        max_retries = 3
        attempts = 0

        while attempts <= max_retries:
            try:
                processor(self.jobs[job_id])
                return True
            except Exception:
                self.retry_counts[job_id] += 1
                attempts += 1
                if attempts <= max_retries:
                    delay = 1 * (2 ** (attempts - 1))
                    self.backoff_delays[job_id] = delay
                else:
                    return False
        return False
```
</content>