Skip to content

interlens.participant.participants.api_client

interlens.participant.participants.api_client

AnthropicClient

AnthropicClient(**kwargs)

Bases: _RetryingClient

Claude via the anthropic SDK (the default provider). Uses Anthropic's separate system param.

Source code in src/interlens/participant/participants/api_client.py
def __init__(self, **kwargs):
	super().__init__(**kwargs)
	import anthropic

	self._anthropic = anthropic
	self._client = anthropic.Anthropic(max_retries=0)  # we own the retry loop, disable the SDK's

submit_batch

submit_batch(
    requests: list[dict], *, poll_interval: float = 30.0
) -> list[str]

Anthropic Message Batches API: one messages.batches.create submits every request (tagged with a positional custom_id), then poll retrieve until processing_status == 'ended' and stream results back, reassembled into input order. A non-succeeded per-request result raises.

Source code in src/interlens/participant/participants/api_client.py
def submit_batch(self, requests: list[dict], *, poll_interval: float = 30.0) -> list[str]:
	"""Anthropic **Message Batches API**: one ``messages.batches.create`` submits every request (tagged with a
	positional ``custom_id``), then poll ``retrieve`` until ``processing_status == 'ended'`` and stream
	``results`` back, reassembled into input order. A non-succeeded per-request result raises."""
	batch = self._client.messages.batches.create(requests=[
		{"custom_id": f"req-{i}",
		 "params": {"model": r["model"], "max_tokens": r["max_tokens"], "temperature": r["temperature"],
		            "messages": r["messages"],
		            **({"system": r["system"]} if r.get("system") else {})}}
		for i, r in enumerate(requests)])
	while self._client.messages.batches.retrieve(batch.id).processing_status != "ended":
		time.sleep(poll_interval)  # await external batch completion (not a fixed delay)
	texts: dict[str, str] = {}
	for entry in self._client.messages.batches.results(batch.id):
		if entry.result.type != "succeeded":
			raise RuntimeError(f"Anthropic batch request {entry.custom_id} did not succeed: {entry.result.type}")
		msg = entry.result.message
		texts[entry.custom_id] = "".join(b.text for b in msg.content if getattr(b, "type", None) == "text")
	return [texts[f"req-{i}"] for i in range(len(requests))]

OpenAIClient

OpenAIClient(
    base_url: str | None = None,
    api_key: str | None = None,
    **kwargs
)

Bases: _OpenAICompatClient

OpenAI directly via the openai SDK (provider="openai"). Reads OPENAI_API_KEY. Supports the asynchronous Batch API for large rollouts.

Source code in src/interlens/participant/participants/api_client.py
def __init__(self, base_url: str | None = None, api_key: str | None = None, **kwargs):
	super().__init__(**kwargs)
	import openai

	self._openai = openai
	key = api_key or os.environ.get(self._api_key_env)
	if not key:
		raise RuntimeError(f"{self._label} needs {self._api_key_env} in the environment (or pass api_key=).")
	self._client = openai.OpenAI(base_url=base_url or self._base_url, api_key=key, max_retries=0)

submit_batch

submit_batch(
    requests: list[dict], *, poll_interval: float = 30.0
) -> list[str]

OpenAI Batch API: upload a JSONL of /v1/chat/completions requests (positional custom_id), batches.create with a 24h window, poll until status == 'completed', then download + parse the output file back into input order. A failed/expired/cancelled batch raises.

Source code in src/interlens/participant/participants/api_client.py
def submit_batch(self, requests: list[dict], *, poll_interval: float = 30.0) -> list[str]:
	"""OpenAI **Batch API**: upload a JSONL of ``/v1/chat/completions`` requests (positional ``custom_id``),
	``batches.create`` with a 24h window, poll until ``status == 'completed'``, then download + parse the
	output file back into input order. A failed/expired/cancelled batch raises."""
	import io
	import json

	lines = [json.dumps({
		"custom_id": f"req-{i}", "method": "POST", "url": "/v1/chat/completions",
		"body": {"model": r["model"], self._tokens_param: r["max_tokens"], "temperature": r["temperature"],
		         "messages": self._full_messages(r.get("system"), r["messages"])}})
		for i, r in enumerate(requests)]
	upload = self._client.files.create(
		file=("batch.jsonl", io.BytesIO("\n".join(lines).encode())), purpose="batch")
	batch = self._client.batches.create(
		input_file_id=upload.id, endpoint="/v1/chat/completions", completion_window="24h")
	while True:
		batch = self._client.batches.retrieve(batch.id)
		if batch.status == "completed":
			break
		if batch.status in ("failed", "expired", "cancelled", "cancelling"):
			raise RuntimeError(f"OpenAI batch {batch.id} ended as {batch.status}")
		time.sleep(poll_interval)  # await external batch completion (not a fixed delay)
	texts: dict[str, str] = {}
	content = self._client.files.content(batch.output_file_id).text
	for line in content.splitlines():
		if not line.strip():
			continue
		obj = json.loads(line)
		texts[obj["custom_id"]] = obj["response"]["body"]["choices"][0]["message"]["content"] or ""
	return [texts[f"req-{i}"] for i in range(len(requests))]

OpenRouterClient

OpenRouterClient(
    base_url: str | None = None,
    api_key: str | None = None,
    **kwargs
)

Bases: _OpenAICompatClient

OpenRouter (https://openrouter.ai) via the OpenAI-compatible openai SDK — one endpoint proxying many providers' models (e.g. anthropic/claude-sonnet-5, openai/gpt-5, meta-llama/llama-3.1-70b-instruct). Reads OPENROUTER_API_KEY. OpenRouter has no batch API, so submit_batch inherits the base's raise — requesting batch mode on an OpenRouter participant fails loudly.

Source code in src/interlens/participant/participants/api_client.py
def __init__(self, base_url: str | None = None, api_key: str | None = None, **kwargs):
	super().__init__(**kwargs)
	import openai

	self._openai = openai
	key = api_key or os.environ.get(self._api_key_env)
	if not key:
		raise RuntimeError(f"{self._label} needs {self._api_key_env} in the environment (or pass api_key=).")
	self._client = openai.OpenAI(base_url=base_url or self._base_url, api_key=key, max_retries=0)