Skip to main content

Vendor Agnostic Agent SDK

Project description

glyph

Minimal vendor-agnostic async SDK that normalizes Claude Agent SDK and OpenAI Agents SDK output into one event stream.

Install

pip install glyph-agents
# or for local development
pip install -e .

Requires Python >=3.10.

Quickstart (query helper)

import asyncio

from glyph import AgentOptions, AgentQueryCompleted, AgentText, query


async def main() -> None:
    options = AgentOptions(
        model="gpt-4.1-mini",  # or "claude-haiku-4-5"
        instructions="You are concise and accurate.",
    )

    async for event in query(
        prompt="In one sentence, explain what an API is.",
        options=options,
    ):
        if isinstance(event, AgentText):
            print(event.text, end="")
        elif isinstance(event, AgentQueryCompleted):
            print("\n\nis_error:", event.is_error)
            print("usage:", event.usage)
            print("total_cost_usd:", event.total_cost_usd)


if __name__ == "__main__":
    asyncio.run(main())

Streaming with GlyphClient

Use GlyphClient when you want explicit control of turn lifecycle methods:

  • query(...) then receive_response(...): send one prompt now, stream that prompt's events right after.
  • query_streamed(...): same behavior as above, but in one call.
  • query_and_receive_response(...): run one prompt and return all events at once (no streaming loop).
  • receive_messages(...): use this when you queued multiple prompts with query(...) first and want to drain them in order from a single stream.
import asyncio

from glyph import AgentOptions, AgentQueryCompleted, AgentText, AgentThinking, GlyphClient


async def main() -> None:
    options = AgentOptions(model="gpt-4.1-mini")

    async with GlyphClient(options) as client:
        async for event in client.query_streamed("List two benefits of unit tests."):
            if isinstance(event, AgentThinking):
                print("[thinking]", event.text)
            elif isinstance(event, AgentText):
                print(event.text, end="")
            elif isinstance(event, AgentQueryCompleted):
                print("\n\n[done]", event.is_error, event.stop_reason, event.usage)


if __name__ == "__main__":
    asyncio.run(main())

Event Types

All APIs stream normalized AgentEvent values:

  • AgentText: visible assistant text segments
  • AgentThinking: reasoning/thinking segments when available
  • AgentToolCall: structured tool invocation requests
  • AgentToolResult: structured tool invocation results
  • AgentQueryCompleted: end-of-turn status (is_error, stop_reason, usage, total_cost_usd, extra)

Backend failures are surfaced as AgentQueryCompleted(is_error=True, ...).

AgentOptions

AgentOptions is the shared configuration surface:

  • model (required): determines backend automatically
  • instructions: system prompt / instructions
  • name: OpenAI agent display name (default: "Assistant")
  • cwd: workspace root for tool access
  • allowed_tools: activation allow-list using Claude-style tool names (Read, Write, Edit, Glob, Grep, Bash, WebSearch, WebFetch).
    • Any tool not listed is disabled.
    • None/empty means no built-in tools are activated.
  • permission: PermissionPolicy(edit_ask=True, execute_ask=True, web_ask=True) enables interactive confirmation per capability.
    • edit_ask applies to file mutation actions (Write / Edit).
    • execute_ask applies to command actions (Bash).
    • web_ask applies to web actions (WebSearch / WebFetch) (WebSearch ask is not supported for OpenAI models).
    • Flags default to False, so capabilities are auto-allowed when the corresponding tool is active.
  • approval_handler_edit: custom approval callback for edit/write actions
  • approval_handler_execute: custom approval callback for command execution actions
  • approval_handler_web: custom approval callback for web actions (WebSearch / WebFetch)
  • max_turns: backend turn cap override
  • reasoning_effort / reasoning_summary: OpenAI-only reasoning controls

Approval Handlers (edit vs execute)

When permissions are set to ask, Glyph can call capability-specific approval handlers:

  • approval_handler_edit: used for Write / Edit style operations
  • approval_handler_execute: used for Bash style operations
  • approval_handler_web: used for WebSearch / WebFetch style operations

If a handler is missing, Glyph falls back to interactive TTY approval prompts. In non-interactive contexts (server/worker/CI), missing handlers will cause the action to be denied with a clear error message.

from glyph import AgentOptions, ApprovalDecision, PermissionPolicy


def approve_edit(req):
    # req.capability == "edit"
    return ApprovalDecision(allow=True)


def approve_execute(req):
    # req.capability == "execute"
    commands = (req.payload or {}).get("commands", [])
    allowed = all("rm -rf" not in c for c in commands)
    return ApprovalDecision(
        allow=allowed,
        reason=None if allowed else "Dangerous command blocked",
    )


options = AgentOptions(
    model="gpt-5.4",
    permission=PermissionPolicy(edit_ask=True, execute_ask=True, web_ask=True),
    approval_handler_edit=approve_edit,
    approval_handler_execute=approve_execute,
)

Backend Resolution

resolve_backend(options) infers backend from model:

  • Claude if model contains claude or anthropic
  • OpenAI if model starts with gpt-, o1, o3, o4, or chatgpt

If inference fails, resolve_backend raises ValueError.

Prompt Input Shape

  • Claude backend accepts str or async iterable prompt blocks (Claude-compatible content blocks).
  • OpenAI backend currently supports str prompts only.

Examples

Run from repository root:

python examples/01_query_helper.py
python examples/02_query_streamed.py
python examples/03_query_then_receive_response.py
python examples/04_query_and_receive_response.py
python examples/05_receive_messages_multiple_turns.py
python examples/06_sessions.py
python examples/07_tools_and_permissions.py
python examples/08_openai_reasoning.py
python examples/09_resolve_backend.py
python examples/10_claude_async_prompt_iterable.py
python examples/11_websearch_tool_calls.py
python examples/12_claude_webfetch_tool_calls.py

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

glyph_agents-0.1.0a1.tar.gz (28.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

glyph_agents-0.1.0a1-py3-none-any.whl (33.3 kB view details)

Uploaded Python 3

File details

Details for the file glyph_agents-0.1.0a1.tar.gz.

File metadata

  • Download URL: glyph_agents-0.1.0a1.tar.gz
  • Upload date:
  • Size: 28.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.8

File hashes

Hashes for glyph_agents-0.1.0a1.tar.gz
Algorithm Hash digest
SHA256 7d66fcec09ffafe2967de94d51b845169b50876d45377ff2134f8a89a6821137
MD5 0d66aab926c7b2c8230d5cf5e929336c
BLAKE2b-256 bbf8d968cc79392843bfed21d87bef13a234ace7f9d1f2bca9580de1cb00e316

See more details on using hashes here.

File details

Details for the file glyph_agents-0.1.0a1-py3-none-any.whl.

File metadata

  • Download URL: glyph_agents-0.1.0a1-py3-none-any.whl
  • Upload date:
  • Size: 33.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.8

File hashes

Hashes for glyph_agents-0.1.0a1-py3-none-any.whl
Algorithm Hash digest
SHA256 3bccaff0b9dae5a6782c2166d68916e2d69b79a4895aee71c965578bec39f183
MD5 cd1d7c0fd2434b1e762a29222c3f1343
BLAKE2b-256 3b0806ea58fa9c0cafa2c5a1c363e53827220dbd1d74e05a4b357435f5c5a26f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page