Skip to content

Repository files navigation

ClimateClaw Client

License docs codecov

A Python client library for interacting with the ClimateClaw backend. This library provides both synchronous and asynchronous interfaces for communicating with a ClimateClaw chatbot instance.

Features:

  • Synchronous client (ClimateClaw) with full API support
  • Asynchronous client (AsyncClimateClaw) with full API support
  • OIDC authentication via py-oidc-auth-client
  • Thread management (create, retrieve, list, search, fork, and delete conversation threads)
  • Streaming and non-streaming prompt responses
  • Thread operations (stop active conversations, set thread topics, edit/fork threads)
  • User feedback (submit positive/negative feedback on assistant messages)
  • Rich message types with markdown rendering support

Requirements

  • Python 3.10+

Dependencies

  • httpx - HTTP client for making requests
  • pydantic - Data validation and message modeling
  • py_oidc_auth_client - OIDC authentication handling

Installation

Currently, this package is in development and must be installed from source:

git clone https://github.com/freva-org/climateclaw-client.git
cd climateclaw-client
pip install -e .

Or using uv:

uv pip install -e .

Usage

Initialization

from climateclaw_client.client import ClimateClaw

# Create a client instance
cc = ClimateClaw(
    base_url="https://your-climate-claw-backend.com",
    token_store_path="~/.cache/climateclaw-client/token-store.json",  # Optional: path to store auth tokens
)

# Authenticate with the backend (triggers OIDC flow)
cc.authenticate()

Available Models

# List available chatbot models
models = cc.available_models
print(f"Available models: {models}")

# Set a default model for the client
cc.model = "gpt-4.1"

Prompting the Backend

Non-streamed Response

# Send a prompt and get the complete conversation
conversation = cc.prompt(
    "Please calculate the average temperature over Germany for the years 1990-2020!"
)
# Render the entire answer as a human-readable string
print(conversation)

# Access the individual messages
for message in conversation.messages:
    print(f"{message.variant}: {message.content}")

# Get markdown representation
markdown = conversation.repr_markdown()
print(markdown)

Streamed Response

# Send a prompt with streaming enabled
stream_conv = cc.prompt(
    "Please explain the ENSO phenomenon to me and give examples of how to quantify it!", stream=True
)

# Iterate over markdown-ready chunks as they arrive
with stream_conv as stream:
    for markdown_chunk in stream.iter_for_markdown():
        print(markdown_chunk)

# After streaming completes, access the full conversation
full_conversation = stream_conv.translate_to_conversation()
print(full_conversation.repr_markdown())

Thread Management

Create a New Thread

# Create a new conversation thread
thread_id = cc.newthread()
print(f"New thread ID: {thread_id}")

Get a Thread

# Retrieve an existing thread by ID
thread_id = "your-thread-id"
conversation = cc.getthread(thread_id=thread_id)

# Or use the current active thread
conversation = cc.getthread()

# Print all messages
print(conversation)

Prompt in a Specific Thread

# Continue a conversation in an existing thread
response = cc.prompt(
    "Please explain how the SOI can be calculated and run an example analysis.",
    thread_id=thread_id,  # optional: uses thread of active conversation otherwise
)

List User Threads

# List all your conversation threads
total_threads, user_threads = cc.getuserthreads(num_threads=10)
print(f"A total number of {total_threads} threads was retrieved.")
# access individual threads (which are Conversation objects)
print(user_threads[0])

Search Threads

# Search for threads by topic
total_results, matching_threads = cc.searchthreads(query="climate analysis", num_threads=5)

Set Thread Topic

# Set a topic for a thread (useful for searching later)
cc.setthreadtopic("ENSO analysis", thread_id=thread_id)

Delete a Thread

# Delete a thread on the backend when you're done with it
cc.deletethread(thread_id=thread_id)

Working with Message Types

The client provides rich message types that can be rendered in different formats:

from climateclaw_client.client import ClimateClaw

# Create a client instance
cc = ClimateClaw(base_url="https://your-climate-claw-backend.com")

# Authenticate with the backend
cc.authenticate()

# List available models
print(f"Available models: {cc.available_models}")
cc.model = cc.available_models[0]

# Start a conversation
response = cc.prompt(
    "Show me a code example of using the xarray library for analysing climate data!"
)

# Access individual messages
initial_response = response[0]

# String representation (for Python sessions)
print(str(initial_response))

# Markdown representation (for rendering)
print(initial_response.repr_markdown())

# Access content directly
print(initial_response.content)

# Extract code cells from Assistant messages
for code_cell in initial_response.message.code_cells:
    print(f"Code cell: {code_cell}")

Handling Images

Image messages have special methods:

# If the response contains an image
if response.messages[1].variant == "Image":
    image_message = conversation.messages[1]

    # Get markdown representation (base64 embedded)
    md = image_message.repr_markdown()

    # Save to file
    image_message.save_to_file("output.png")

Raw Message Access

# Access raw message chunks (before aggregation)
response = cc.prompt("Hello ClimateClaw! What is your function?")

for raw_msg in conversation.raw_messages:
    print(raw_msg.message.variant)
    print(raw_msg.message.content)

Message Types

The library supports the following message variants:

Variant Description Special Methods
Prompt Initial user prompt repr_content(), repr_markdown()
User User message repr_content(), repr_markdown()
Assistant Assistant response code_cells property, repr_content(), repr_markdown()
Code Python code code_cells property, repr_markdown() renders as code block
CodeOutput Code execution output repr_markdown() renders as blockquote
Image Base64-encoded image repr_markdown(), save_to_file()
ServerError Server error message repr_content(), repr_markdown()
OpenAIError OpenAI error message repr_content(), repr_markdown()
CodeError Code execution error repr_content(), repr_markdown()
StreamEnd Stream completion marker repr_markdown()
ServerHint Backend hint data (such as server heartbeats) repr_markdown()

Asynchronous Client

The library includes an AsyncClimateClaw class for async operations, providing the same functionality as the synchronous client but with async/await syntax:

import asyncio
from climateclaw_client import AsyncClimateClaw


async def main():
    # Create an async client instance
    cc = AsyncClimateClaw(
        base_url="https://your-climate-claw-backend.com",
        token_store_path="~/.cache/climateclaw-client/token-store.json",
    )

    # Authenticate with the backend
    await cc.authenticate()

    # List available models
    print(f"Available models: {cc.available_models}")
    cc.model = cc.available_models[0]

    # Send a prompt
    response = await cc.prompt(
        "Please calculate the average temperature over Germany for 1990-2020!"
    )
    print(response)

    # Send a streaming prompt
    stream_resp = await cc.prompt("Please explain the ENSO phenomenon to me!", stream=True)
    async with stream_resp as stream:
        async for markdown_chunk in stream.aiter_for_markdown():
            print(markdown_chunk)

    # Thread management
    thread_id = await cc.newthread()
    response = await cc.prompt(
        "Please explain how the SOI can be calculated.",
        thread_id=thread_id,
    )


# Run the async main function
asyncio.run(main())

Note: The async client uses httpx.AsyncClient under the hood and provides all the same methods as the synchronous ClimateClaw client, but as coroutines that must be awaited.

Configuration Options

Parameter Type Default Description
base_url str/URL Required Base URL of the ClimateClaw backend
token_store_path str "" Path to store OIDC tokens
follow_redirects bool True Whether to follow HTTP redirects
timeout float 30.0 Request timeout in seconds
max_retries int 3 Maximum retry attempts for failed requests
http_client httpx.Client / httpx.AsyncClient None Pre-configured HTTP client (Optional)
thread_id str None Default thread ID for conversations
model str None Default model for prompts

Note: For AsyncClimateClaw, the http_client parameter should be an httpx.AsyncClient instance, while for ClimateClaw it should be an httpx.Client instance.

Project Links

License

This project is licensed under the European Union Public Licence 1.2 (EUPL-1.2).

About

Python Client Library for ClimateClaw

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages