# Python API Reference The `openai-harmony` package exposes the Harmony renderer to Python via thin bindings generated with `PyO3`. It installs a module named `openai_harmony` which re‑exports a set of dataclasses mirroring the structures from the Rust crate together with helper classes for encoding and parsing. ## Installation Install the package from PyPI: ```bash pip install openai-harmony ``` Typical imports look like: ```python from openai_harmony import Message, Conversation, load_harmony_encoding ``` ## Enumerations ### `Role` Represents the author of a message. Possible values are `USER`, `ASSISTANT`, `SYSTEM`, `DEVELOPER` and `TOOL`. ### `ReasoningEffort` Defines how much reasoning the assistant should apply. Values: `LOW`, `MEDIUM`, `HIGH`. ### `StreamState` State of an incremental parser: `EXPECT_START`, `HEADER` or `CONTENT`. ## Dataclasses ### `Author` ```python Author(role: Role, name: Optional[str] = None) ``` Helper for specifying the author of a message. Use `Author.new(role, name)` to create a named author. ### `TextContent` ```python TextContent(text: str) ``` Simple text payload implementing `Content`. ### `ToolDescription` ```python ToolDescription(name: str, description: str, parameters: Optional[dict] = None) ``` Describes an individual callable tool. ### `ToolNamespaceConfig` ```python ToolNamespaceConfig(name: str, description: Optional[str], tools: List[ToolDescription]) ``` Namespace for grouping tools. Convenience constructors `browser()` and `python()` return the built‑in configurations. ### `ChannelConfig` ```python ChannelConfig(valid_channels: List[str], channel_required: bool) ``` Configuration for valid message channels. Use `ChannelConfig.require_channels(channels)` to demand that a specific set of channels is present. ### `SystemContent` ```python SystemContent( model_identity: Optional[str] = "You are ChatGPT, a large language model trained by OpenAI.", reasoning_effort: Optional[ReasoningEffort] = ReasoningEffort.MEDIUM, conversation_start_date: Optional[str] = None, knowledge_cutoff: Optional[str] = "2024-06", channel_config: Optional[ChannelConfig] = ChannelConfig.require_channels(["analysis", "commentary", "final"]), tools: Optional[dict[str, ToolNamespaceConfig]] = None, ) ``` Represents a system message. Provides fluent helpers like `with_model_identity()`, `with_reasoning_effort()`, `with_required_channels()`, `with_browser_tool()` and `with_python_tool()`. ### `DeveloperContent` ```python DeveloperContent(instructions: Optional[str] = None, tools: Optional[dict[str, ToolNamespaceConfig]] = None) ``` Content of a developer message. Helper methods include `with_instructions()`, `with_function_tools()` and the tool helpers mirroring those from `SystemContent`. ### `Message` ```python Message(author: Author, content: List[Content], channel: Optional[str] = None, recipient: Optional[str] = None, content_type: Optional[str] = None) ``` A single chat message. Convenience constructors `from_role_and_content()` and `from_role_and_contents()` mirror the Rust API. Builder methods allow adding content or setting channel/recipient information. `to_dict()` and `from_dict()` serialise to/from the canonical JSON format. ### `Conversation` ```python Conversation(messages: List[Message]) ``` Sequence of messages. Create a conversation using `Conversation.from_messages()`; serialisation helpers `to_json()` and `from_json()` mirror the Rust crate. ### `RenderConversationConfig` ```python RenderConversationConfig(auto_drop_analysis: bool = True) ``` Optional configuration when rendering a conversation. ## Encoding helpers ### `HarmonyEncoding` Wrapper around the low level bindings. Obtain an instance with `load_harmony_encoding()`. Methods: - `name` – name of the encoding. - `render_conversation_for_completion(conversation, next_turn_role, config=None)` – render a conversation into tokens. - `render_conversation_for_training(conversation, config=None)` – render a conversation for training. - `render_conversation(conversation, config=None)` – render a conversation without appending a new role. - `render(message)` – render a single message into tokens. - `parse_messages_from_completion_tokens(tokens, role=None)` – parse tokens back into `Message` objects. - `decode_utf8(tokens)` – decode tokens with the underlying tokenizer. - `stop_tokens()` / `stop_tokens_for_assistant_actions()` – lists of stop tokens. ### `StreamableParser` Incremental parser built on top of an encoding. Construct with `StreamableParser(encoding, role)` and feed tokens via `process(token)`. Inspect state via properties like `current_content`, `current_role`, `tokens` and `state`. ### `load_harmony_encoding(name)` Return a `HarmonyEncoding` by name. Accepts either the string name or a value from the `HarmonyEncodingName` enum (`HARMONY_GPT_OSS`). ## Exports The package re‑exports the above classes through `__all__` so they are available via: ```python from openai_harmony import * ``` ## Usage Examples Create a conversation with a system and user message, render it to tokens and parse them back again: ```python from openai_harmony import ( Role, Message, Conversation, SystemContent, load_harmony_encoding, HarmonyEncodingName, ) # Build messages system = Message.from_role_and_content(Role.SYSTEM, SystemContent.new()) user = Message.from_role_and_content(Role.USER, "What is 2 + 2?") # Assemble a conversation convo = Conversation.from_messages([system, user]) # Render to tokens using the OSS encoding enc = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS) tokens = enc.render_conversation_for_completion(convo, Role.ASSISTANT) print(tokens) # Decode and roundtrip print(enc.decode_utf8(tokens)) parsed = enc.parse_messages_from_completion_tokens(tokens, role=Role.ASSISTANT) for m in parsed: print(m) ``` ## Exceptions The bindings raise plain Python exceptions. The most common ones are: - `RuntimeError` – returned for rendering or parsing failures (for example if a token sequence is malformed or decoding fails). - `ValueError` – raised when an argument is invalid, e.g. an unknown `Role` is provided to `load_harmony_encoding` or `StreamableParser`. - `ModuleNotFoundError` – accessing the package without building the compiled extension results in this error. In typical code you would wrap encoding operations in a `try`/`except` block: ```python try: tokens = enc.render_conversation_for_completion(convo, Role.ASSISTANT) parsed = enc.parse_messages_from_completion_tokens(tokens, Role.ASSISTANT) except RuntimeError as err: print(f"Failed to handle conversation: {err}") ```