Vendor Agnostic Agent SDK
Project description
Glyph
Glyph is a vendor-agnostic Python SDK and CLI for building agent workflows with OpenAI and Anthropic models.
It is designed for flows where some steps should stay deterministic and only the parts that actually need an LLM should call one.
Glyph has two core use cases:
- A vendor-agnostic agent SDK
- A workflow builder that can act like a
SKILL.mdexecutor
Markdown Workflow Example
This example demonstrates how Glyph can execute workflows described entirely in Markdown.
Each step in your workflow can do one of the following:
- If it includes an
execute:key, or contains a Python or Bash code block, Glyph will run the code automatically for you. - If the step contains plain text, Glyph treats it as a prompt to the selected language model and generates a response.
This lets you blend deterministic logic and LLM-powered actions seamlessly in a single document.
---
name: writePostcard
description: if the user asks for a postcard, follow these steps
options:
model: gpt-5.4-mini
reasoning_effort: medium
allowed_tools: [Read, Glob, Grep]
---
## Step: loadTripContext
```python
return {
"city": "Lisbon",
"mood": "warm and nostalgic",
"memory": "the yellow tram climbing the hill at sunset",
}
```
<!-- Each step receives the previous step result automatically. -->
## Step: draftPostcard
<!-- Glyph fills template variables from previous step output. -->
Write a short postcard message from {{ city }}.
The mood should feel {{ mood }}.
Mention this memory: {{ memory }}.
Keep it to 3 sentences maximum.
## Step: savePostcard
```python
from pathlib import Path
output_path = Path(__file__).with_name("postcard.txt")
output_path.write_text(previous_result.message, encoding="utf-8")
return {"file_path": str(output_path)}
```
<!-- This is a hint for readibility purpose and is not required. -->
returns:
file_path: str
Run the workflow with:
glyph workflow.md
Glyph is vendor-agnostic: change options.model and Glyph will infer the
backend from the model name.
Markdown workflows support inline deterministic steps with fenced python or
bash blocks. Inline bash steps run in the workflow directory and expose the
previous step result as GLYPH_PREVIOUS_RESULT_JSON.
Install
pip install glyph-agents # install in a virtualenv if you want the SDK
pipx install glyph-agents # install only the glyph CLI
Requires Python >=3.10.
Glyph Python SDK
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(...)thenreceive_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 withquery(...)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 segmentsAgentThinking: reasoning/thinking segments when availableAgentToolCall: structured tool invocation requestsAgentToolResult: structured tool invocation resultsAgentQueryCompleted: 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 automaticallyinstructions: system prompt / instructionsname: OpenAI agent display name (default:"Assistant")cwd: workspace root for tool accessallowed_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_askapplies to file mutation actions (Write/Edit).execute_askapplies to command actions (Bash).web_askapplies to web actions (WebSearch/WebFetch) (WebSearchask 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 actionsapproval_handler_execute: custom approval callback for command execution actionsapproval_handler_web: custom approval callback for web actions (WebSearch/WebFetch)max_turns: backend turn cap overridebash_timeout_ms: OpenAI Bash tool default timeout overridereasoning_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 forWrite/Editstyle operationsapproval_handler_execute: used forBashstyle operationsapproval_handler_web: used forWebSearch/WebFetchstyle 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,
)
Workflows
GlyphWorkflow lets you compose multi-step flows where each step receives the
previous step result. Define steps with @step, or put the workflow in Markdown
and run it with glyph workflow.md.
import asyncio
import os
from glyph import AgentOptions, AgentQueryCompleted, GlyphWorkflow, step
class MyWorkflow(GlyphWorkflow):
options = AgentOptions(model=os.getenv("GLYPH_MODEL", "gpt-4.1-mini"))
@step
async def load_topic(self) -> str:
return "sea turtles"
@step(prompt="Write one short sentence about {topic}.")
async def ask_model(self, topic: str) -> None:
self.fill_prompt(topic=topic)
result: AgentQueryCompleted = yield
print(result.message)
async def main() -> None:
await MyWorkflow.run()
if __name__ == "__main__":
asyncio.run(main())
Python workflows (@step)
@step— plain Python step.@step(prompt=..., model=..., is_streaming=...)— LLM step;modeloverrides the workflow default for that step only.- LLM steps: set up
self.promptin the method body (fill_prompt, etc.). If the method does not useyield, the query runs after it returns. If it usesyield(async generator), that starts the turn: withis_streaming=Falseyou typically getAgentQueryCompletedafter the firstyield; withis_streaming=Trueeachyieldreceives the next streamedAgentEventuntilAgentQueryCompleted(seeexamples/16_workflow_streaming.py). self.fill_prompt(...)— render prompt templates without failing on missing placeholders (they stay in the text).self.next_step(self.some_step, value)— jump to another step with an explicit input.self.stop_workflow(value)— end the workflow immediately;GlyphWorkflow.run(...)returnsvalue.GlyphWorkflow.run(options=..., initial_input=..., session_id=...)— runtime overrides and optional first-step input.
Markdown workflows (## Step:)
GlyphWorkflow.from_markdown(path)andrun_markdown_workflow(path, ...)load a linear workflow from## Step:sections.- The first
## Step:is the entrypoint. - Each step can be an LLM prompt, an inline Python block, or an
execute:mapping (file plus optional function). <!-- ... -->comments are ignored by the loader.
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_webfetch_tool_calls.py
python examples/13_basic_workflow.py
python examples/14_workflow_context.py
python examples/15_workflow_init_override.py
python examples/16_workflow_streaming.py
glyph examples/17_workflow_markdown/workflow.md
glyph examples/18_workflow_mardown_python/workflow.md
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file glyph_agents-0.1.0.tar.gz.
File metadata
- Download URL: glyph_agents-0.1.0.tar.gz
- Upload date:
- Size: 45.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b8a3e9687d108830258c439b135da47b1689cfbcf9a5b03cc83894443bbb7194
|
|
| MD5 |
5a31661fab8e79e2a5bc2c5e6408849f
|
|
| BLAKE2b-256 |
f24ea33d81ee2a537382b949c6f929b9b6387e86a6722a438940aae5eda220f1
|
File details
Details for the file glyph_agents-0.1.0-py3-none-any.whl.
File metadata
- Download URL: glyph_agents-0.1.0-py3-none-any.whl
- Upload date:
- Size: 51.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bd198e8911fbcc38e7bf746a46dff67bcd429b728f90bec020063db98c8c1530
|
|
| MD5 |
eede39eda3295beeed12ab3ba1e1ea1a
|
|
| BLAKE2b-256 |
33677c92e04f65b8c928d7a3b3a8fb84359ddf2c6d1dd355b4b0a7908979cbc1
|