Skip to main content
Input & Output

Multi-turn Conversation

To send multiple user messages within the same session, use QoderSDKClient — it maintains a long-lived connection and lets you decide the next message based on the model's reply. For one-shot, stateless queries, use query() (see Quick Start).

Multi-message session

Each call to client.query(...) appends one turn of input; consume the reply to its end with client.receive_response():
import anyio

from qoder_agent_sdk import (
    AssistantMessage,
    QoderAgentOptions,
    QoderSDKClient,
    ResultMessage,
    TextBlock,
    access_token_from_env,
)


async def main():
    options = QoderAgentOptions(auth=access_token_from_env())

    async with QoderSDKClient(options=options) as client:
        await client.query("What is the capital of France?")
        async for msg in client.receive_response():
            if isinstance(msg, AssistantMessage):
                for block in msg.content:
                    if isinstance(block, TextBlock):
                        print(f"Assistant: {block.text}")

        # Choose the next message based on the previous response.
        await client.query("What is the population of this city?")
        async for msg in client.receive_response():
            if isinstance(msg, ResultMessage):
                print(f"Done: {msg.subtype}")


anyio.run(main)

Steering a response

Call client.query(...) again while the current response is being consumed to send another message over the same long-lived session:
await client.query(
    "Stop the current direction and analyze only the failing tests.",
    priority="now",
)
priority controls when a message is delivered:
ValueBehavior
"now"Stop the current response and handle this message immediately
"next"Default; handle this message at the next suitable point
"later"Wait until the current response finishes
Messages with the same priority run in send order. Use priority="now" to change direction immediately. To stop the current response without sending another message, use client.interrupt().

Add context without starting a response

should_query=False adds the message to the conversation without starting a response by itself. Its processing time still follows priority.
await client.query(
    "All subsequent suggestions must be compatible with Python 3.10.",
    should_query=False,
)

Interrupting the current response

Only QoderSDKClient exposes runtime interruption; the one-shot query() iterator does not. Call await client.interrupt() to stop the current response without disconnecting the client. You can continue the conversation afterward.
import asyncio

from qoder_agent_sdk import QoderAgentOptions, QoderSDKClient, qodercli_auth


async def receive_turn(client):
    return [msg async for msg in client.receive_response()]


async def main():
    options = QoderAgentOptions(auth=qodercli_auth())
    async with QoderSDKClient(options=options) as client:
        await client.query("Inspect all files in the repository.")
        response_task = asyncio.create_task(receive_turn(client))

        await asyncio.sleep(5)
        await client.interrupt()
        await response_task


asyncio.run(main())
interrupt() does not clear later queued messages or disconnect the client, so it can accept another turn.

Cancel a queued message

Give each tracked message a session-unique message_uuid, then call client.cancel_async_message(message_uuid) to cancel it before execution starts:
await client.query(
    "Create a migration checklist after the current task is complete.",
    priority="later",
    message_uuid="optional-follow-up",
)

cancelled = await client.cancel_async_message("optional-follow-up")
The method returns True when the message is cancelled and False when the message is not found or can no longer be cancelled. Messages without a UUID cannot be cancelled this way. Do not reuse UUIDs within a session.

Managing the session lifecycle

The connection lifecycle of QoderSDKClient is owned by the caller. Two ways to manage it: Use this when the session's lifetime is bound to a function or block scope:
async with QoderSDKClient(options=options) as client:
    await client.query("Hello")
    async for msg in client.receive_response():
        ...
# The connection closes automatically when the async with block exits.

Manual management

Use this when the client is held by a long-lived object, or when closure must be triggered by an external condition (timeout, user cancellation, etc.):
client = QoderSDKClient(options=options)
await client.connect()

try:
    await client.query("Hello")
    async for msg in client.receive_response():
        ...
finally:
    await client.disconnect()
Using Qoder CLI
Configuration & Security
Reference
SDK