All integrations

Together AI

Together fine-tunes / batch jobs - completion without polling.

PreviewServicechirp login
together (running) Live Activity preview
what shows on your phone

Together AI's outbound webhook system is still in private preview, so the cleanest path today is in-process: wrap your fine-tune submission with `chirp.activity` and stream Together's job events into the activity's `update()` method. Same shape as the Modal walkthrough - the difference is you're tailing Together's event stream instead of looping over your own batches.

Once Together ships native outbound webhooks, this integration will switch to the @huggingface model (paste a URL in their dashboard). The current SDK-based pattern keeps working either way - Chirp's @together schema accepts both.

Prerequisites

  • A Together account + `TOGETHER_API_KEY`.
  • Chirp Python SDK (`pip install chirp-sdk`) + `chirp login` locally.

Setup

  1. 1

    Install both SDKs

    Together's SDK plus Chirp's SDK. Both are pure-Python, no native deps.

    shell
    pip install together chirp-sdk
  2. 2

    Pair Chirp

    chirp login once on the laptop running the script. Drops credentials at ~/.chirp/credentials. The SDK reads them automatically.

    shell
    chirp login
  3. 3

    Wrap the fine-tune with chirp.activity

    Open the activity on submission, stream Together's events, close on context exit. Together's list_events(stream=True) blocks until the job terminates, which lines up perfectly with the activity's lifecycle.

    together_job.py
    import chirp
    from together import Together
    
    client = Together()
    
    with chirp.activity("@together", {
        "jobName": "qwen-finetune",
        "gpuType": "H100",
        "model":   "Qwen/Qwen2.5-7B",
    }) as a:
        job = client.fine_tuning.create(
            model="Qwen/Qwen2.5-7B",
            training_file="file-abc123",
            n_epochs=3,
        )
        a.update({"stageNote": "submitted", "jobId": job.id})
    
        for ev in client.fine_tuning.list_events(id=job.id, stream=True):
            a.update({"stageNote": ev.message})
            # Optional: parse progress from ev.message and pass it along.
    
        # Context manager closes the card on exit (green if no exception).
  4. 4

    (Optional) Run from a long-lived script with retries

    If your fine-tune script lives on a server (not your laptop), run it under tmux or as a systemd unit. The activity continues across SSH disconnects since it's the *server* that holds the activity token.

What you’ll see

Card header: Together logo + "Together · TRAINING" + jobName. Action line shows GPU type + current stage from Together's event stream ("queued", "loading data", "epoch 1/3", etc.). Closes green on clean exit; red with the exception on raise. If your script crashes mid-job, the activity will hang at the last update - restart the script and pass the same `jobName` to coalesce on the existing card.

Troubleshooting

list_events hangs forever even though the job is done.
Together's stream sometimes doesn't emit a terminal event when the job completes via the dashboard's "cancel" button. Set a timeout: for ev in client.fine_tuning.list_events(id=job.id, stream=True, timeout=30): and break manually if client.fine_tuning.retrieve(job.id).status is terminal.
Card opens with no GPU type.
GPU type isn't in Together's event payload - pass it manually based on which model you're tuning. Reference: 7B → A100, 70B → H100. The schema renders the value verbatim.
I want to use this for batch inference, not fine-tunes.
Same pattern, swap client.fine_tuning.create for client.batches.create and tail client.batches.retrieve(batch.id) in a polling loop. The @together schema renders "BATCH" instead of "TRAINING" when kind: "batch" is passed.
External docs →