OpenAIInterface
On this page
llm.OpenAIInterface — sync & async OpenAI Chat Completions interface with native token streaming.
Constructor
OpenAIInterface(
model_name: str = "gpt-4",
api_key: Optional[str] = None,
**kwargs,
)
Creates both an openai.OpenAI (sync) and openai.AsyncOpenAI (async) client eagerly, both with the same api_key. If the openai package isn't installed, raises ImportError("install openai: pip install openai").
generate(messages=None, prompt=None, max_tokens=2048, temperature=0.3, tools=None, tool_choice=None, **kwargs) -> Union[str, dict]
Synchronous call to chat.completions.create.
Parameters
Returns: If the model doesn't call a tool, r.choices[0].message.content — a plain str, exactly as without tools. If the model calls one or more tools, a dict: {"content": ..., "tool_calls": [{"id", "name", "arguments"}, ...], "stop_reason": r.choices[0].finish_reason}, built by llm.tools.parse_openai_tool_calls + tool_calls_to_dicts — arguments is already parsed from OpenAI's JSON-string form into a dict. On failure, a dict: {"error": str(e)} — note the return type is not uniform; calling code should check isinstance(result, dict) and then distinguish an error dict ("error" key) from a tool-call dict ("tool_calls" key) if it needs to. See Tool Calling for the full contract.
Raises: nothing directly — a missing prompt/messages does trigger _build_messages to raise ValueError("Either 'prompt' or 'messages' must be provided."), but that call happens inside this method's own try/except, so the ValueError is caught along with every other exception from the OpenAI SDK, logged via logger.error(...), and converted into the {"error": ...} return value — it does not propagate to the caller. (Verified against the source and empirically: OpenAIInterface(api_key="...").generate() with no arguments returns {"error": "Either 'prompt' or 'messages' must be provided."} rather than raising.)
Example
from zerda.llm import OpenAIInterface
llm = OpenAIInterface(api_key="sk-...")
result = llm.generate(prompt="Summarize RAG in one sentence.")
if isinstance(result, dict):
print("Error:", result["error"])
else:
print(result)
generate_async(messages=None, prompt=None, max_tokens=2048, temperature=0.3, tools=None, tool_choice=None, **kwargs) -> Union[str, dict]
Identical contract to generate, but calls await self._async_client.chat.completions.create(...). Same return-shape caveat applies, including the tool-call dict shape.
astream(messages=None, prompt=None, max_tokens=2048, temperature=0.3, **kwargs) -> AsyncIterator[str]
Native incremental streaming — passes stream=True to chat.completions.create and yields each chunk's delta.content as it arrives:
async for token in llm.astream(prompt="Write a haiku about the desert."):
print(token, end="", flush=True)
On error, yields a single string f"Error: {e}" instead of raising — the generator does not raise; the error surfaces as stream content.
astream does not accept tools/tool_choice as named parameters and does not parse tool-call deltas — a tools= kwarg would be forwarded via **kwargs straight into the streaming chat.completions.create call, but any tool-call chunks in the resulting stream are not reconstructed into the unified tool_calls shape. Use generate/generate_async for tool calling.
cleanup() / acleanup()
def cleanup(self): # closes self._sync_client
async def acleanup(self): # closes self._async_client
Both wrap the close call in try/except, logging a warning (not raising) if it fails. Call cleanup() when you're done with the sync client, and either await llm.acleanup() or use async with OpenAIInterface(...) as llm: for the async client.
Thread safety
Not explicitly documented; relies on the underlying openai SDK client's own thread-safety guarantees, which this wrapper does not add to or verify.
Related APIs
BaseLLMInterface— the parent contract.GroqInterface— nearly identical shape, since Groq's SDK mirrors OpenAI's.AnthropicInterface— sameprompt/messagespattern, different provider.- Tool Calling — the unified
tools/tool_choicecontract and per-provider conversion table.