Lightweight AI flows, agents, tools, and model adapters for event-driven Python systems.
Project description
modmex-ai
Lightweight, provider-neutral agents, flows, tools, and voice runtimes for Python.
modmex-ai is a small runtime for applications that need an agent to reason,
call tools, hand off work, and return a controlled result. It fits equally well
in HTTP APIs, CLIs, background workers, WebSocket servers, notebooks, desktop
applications, voice systems, and event-driven/serverless workloads.
Its core has no provider SDK dependency. OpenAI is the first complete adapter; the core contracts remain reusable for another text, speech, or realtime provider.
The basic execution model is intentionally simple:
input -> Flow -> one or more Agents -> output, tools, handoffs, and events
What it provides
Agentfor instructions, structured output, tools, guardrails, and streaming.Flowfor multi-agent routing, handoffs, continuation, and sync/async execution.- Sessions, snapshots, optimistic concurrency contracts, and approval checkpoints.
- Realtime and chained voice contracts that keep STT, reasoning, and TTS composable.
- Provider-neutral tracing, usage, evals, and host-owned observability.
Install the core package in any Python application:
pip install modmex-ai
Add only the optional transport needed by a deployment:
pip install 'modmex-ai[async]' # native async HTTP
pip install 'modmex-ai[realtime]' # WebSocket-based Realtime adapters
Quick start
from modmex import BaseModel
from modmex_ai import Agent, FakeModel, Flow
class TriageOutput(BaseModel):
intent: str
confidence: float
triage = Agent(
name="triage",
instructions="Classify the incoming message.",
output_type=TriageOutput,
handoffs=["support"],
)
flow = Flow(
name="analyze-message",
entrypoint=triage,
agents=[],
model=FakeModel(['{"intent":"support","confidence":0.91}']),
)
result = flow.run("I need help")
result.output.model_dump()
The same Flow can be called by a REST handler, a queue consumer, a CLI
command, or a long-lived WebSocket connection. Event-driven systems are a
natural fit, but they are not required.
Multi-agent delegation uses handoff function tools, not a field in the model output. A handoff defaults to transfer_to_agent_name; its typed input is validated and delivered to an on_handoff callback before the next agent continues with the conversation.
Durable sessions and approvals
The core provides portable SessionSnapshot and PersistedFlowState models,
plus optimistic-concurrency store contracts. A host chooses the database and
owns the lifecycle; this keeps the framework usable in a web application, a
worker, or serverless infrastructure without importing a storage SDK into the
core.
Tools can require an externally verified approval. When a protected tool is
requested, a Flow raises FlowSuspended with the exact pending tool call and
a serializable checkpoint. Persist it, collect the signed approval in the
host, then resume the Flow without asking the model to decide again.
modmex-lambda includes optional DynamoDB store adapters for Lambda-oriented
hosts. Other applications can implement the same DurableSessionStore and
FlowStateStore contracts with Postgres, Redis, or their existing storage.
Realtime voice sessions
VoiceAgentSession is the common contract for voice sessions. There are two
implementations with distinct responsibilities:
OpenAIRealtimeSessionpara speech-to-speech por WebSocket/SIP.ChainedVoiceSessionpara conservar el control explícito de STT →Agent/Flow→ TTS.
Ambas exponen eventos normalizados, uso acumulado, herramientas y handoffs. El
adaptador de OpenAI conserva el payload específico del proveedor únicamente en
su frontera; el core usa tool_call_id para no confundir llamadas de tools con
identificadores de llamadas telefónicas.
Install the optional WebSocket transport only in workloads that need it:
pip install 'modmex-ai[realtime]'
The OpenAI adapter reuses Agent tools, handoffs, tool guardrails, context,
usage, and traces. It does not depend on the OpenAI Agents SDK. For a SIP call
that was already accepted by your server, connect the worker using its
realtime_call_id:
import asyncio
from modmex_ai import Agent
from modmex_ai.providers.openai import (
OpenAIRealtimeClient,
OpenAIRealtimeSessionConfig,
)
async def run_voice_call(realtime_call_id: str) -> None:
agent = Agent(
name="support",
instructions="Help the caller concisely and use tools when needed.",
)
client = OpenAIRealtimeClient()
session = await client.connect(
agent=agent,
realtime_call_id=realtime_call_id,
config=OpenAIRealtimeSessionConfig(voice="marin"),
)
async with session:
await session.configure()
await session.create_response(
instructions="Greet the caller and ask how you can help."
)
async for event in session.events():
if event.type == "response.done":
print(session.usage.model_dump())
asyncio.run(run_voice_call("rtc_u1_example"))
When the model emits a function call, the session validates and executes the
registered Modmex-AI tool, sends its output to the socket, and asks Realtime to
continue. A transfer_to_* handoff updates the active agent's instructions and
tool surface in the same Realtime session.
For a SIP accept endpoint, build the accept body from the exact same session configuration used after the WebSocket connects. That prevents model, voice, instructions and tool definitions from drifting between both phases:
accept_payload = OpenAIRealtimeSessionConfig(voice="marin").to_accept_payload(agent)
Chained voice pipeline
Use a chained session when the application must choose its transcription and
speech providers, or needs text-agent behavior between audio turns. A Flow
is preferred when the conversation can hand off to another agent.
session = ChainedVoiceSession(
flow=carrier_flow,
speech_to_text=transcriber,
text_to_speech=synthesizer,
context={"workspace_id": "workspace-1"},
)
audio_reply = await session.process_audio(caller_audio)
async for event in session.voice_events():
# transcript_final, tool_finished, handoff_completed,
# response_completed, session_ended, or error
handle_voice_event(event)
The STT/TTS providers are small async protocols, so concrete provider SDKs stay
outside the core package. Close either session with a VoiceTerminationReason
to make lifecycle reporting explicit.
For bounded audio turns, the OpenAI adapters use the Audio API directly through the lightweight HTTP client—no provider SDK is installed:
from modmex_ai.providers.openai import (
OpenAISpeechProvider,
OpenAITranscriptionProvider,
)
session = ChainedVoiceSession(
flow=carrier_flow,
speech_to_text=OpenAITranscriptionProvider(model="gpt-4o-mini-transcribe"),
text_to_speech=OpenAISpeechProvider(voice="marin", response_format="wav"),
)
To continue a chained conversation in another process, persist the host's
Session implementation and session.continuation. The continuation tracks
the active agent and provider state; the host owns durable history storage.
For a small self-contained host, SessionSnapshot.from_session(session) can
serialize an InMemorySession and later restore it with
snapshot.to_memory_session().
ChainedVoiceSession.process_audio_stream() supports providers that implement
streaming STT and TTS contracts. It emits transcript deltas, waits for a final
transcript before invoking the text flow, then yields playable audio chunks.
The callable adapters make it possible to plug in a provider-specific function
without making that provider a dependency of modmex-ai.
Native async models
Models can optionally expose async def acomplete(request). Agent.run_async
and Flow.run_async use it directly, including async tools; existing sync
models retain their compatible worker-thread fallback. The built-in OpenAI
clients expose acomplete() as well. Install the optional native transport to
make them select httpx automatically:
pip install 'modmex-ai[async]'
Without that extra, the compatible stdlib transport runs in a worker and keeps the event loop responsive.
Realtime transcription
For live microphone or telephony media, use the transcription-only WebSocket
adapter instead of a speech-to-speech agent session. It emits Transcription
deltas as audio arrives and one final value after manual commit:
from modmex_ai.providers.openai import OpenAIRealtimeTranscriptionProvider
transcriber = OpenAIRealtimeTranscriptionProvider()
async for transcript in transcriber.transcribe_stream(pcm_chunks, context=context):
render(transcript.text, final=transcript.is_final)
The adapter uses 24 kHz PCM by default and requires modmex-ai[realtime].
For completed audio turns, OpenAITranscriptionProvider remains the simpler
request-based alternative.
For a long-lived call, open one reusable session and commit each user turn:
session = await transcriber.connect()
await session.append_audio(pcm_chunk)
await session.commit_turn()
async for transcript in session.events():
correlate(transcript.item_id, transcript.text, transcript.is_final)
Turn cancellation and provider retries
ChainedVoiceSession accepts VoiceTurnOptions(max_provider_retries=...).
Retries apply only to STT/TTS provider I/O, never to the text flow, tools, or
handoffs. Call cancel_current_turn() to cooperatively stop a streaming turn
before its next provider chunk; hosts can still cancel the owning task when an
immediate interruption of a blocked transport is required.
After every completed, cancelled, timed-out, or failed bounded turn,
ChainedVoiceSession.last_turn contains a VoiceTurnOutcome. Its metrics
separate STT, agent, TTS, total latency, and provider retry count.
Pass a VoiceTurnObserver to export those outcomes to the host's own logs or
metrics backend without adding an observability dependency to this package.
Roadmap
Model Context Protocol (MCP) is not supported yet. MCP client and tool-server
integration are planned for a future release; today, applications can register
local tools directly through Agent.
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
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 modmex_ai-0.1.0.tar.gz.
File metadata
- Download URL: modmex_ai-0.1.0.tar.gz
- Upload date:
- Size: 95.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b687dbd3d70571a6ab3aad91a9bd1a1b981654dc91034b259aca623530cad5e4
|
|
| MD5 |
c0cbb3e89bb69464b4d250fc67a28ba3
|
|
| BLAKE2b-256 |
c4c11748957cc2bb940ca78dd3fdb40a04bd7a057bfe8bf99f17631b0aca365f
|
Provenance
The following attestation bundles were made for modmex_ai-0.1.0.tar.gz:
Publisher:
release.yml on modmex/modmex-ai
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
modmex_ai-0.1.0.tar.gz -
Subject digest:
b687dbd3d70571a6ab3aad91a9bd1a1b981654dc91034b259aca623530cad5e4 - Sigstore transparency entry: 2215318098
- Sigstore integration time:
-
Permalink:
modmex/modmex-ai@b4685579a417f7d1f6da8757763e0af9c73a63b5 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/modmex
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b4685579a417f7d1f6da8757763e0af9c73a63b5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file modmex_ai-0.1.0-py3-none-any.whl.
File metadata
- Download URL: modmex_ai-0.1.0-py3-none-any.whl
- Upload date:
- Size: 71.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
70cc16ce7d1392991867120250276eed0abd0b3e49ab7be58815d891bf8eabf0
|
|
| MD5 |
99ae4b435dc0bdfe7f1b007a3f4ffae1
|
|
| BLAKE2b-256 |
60ce768ec76326445baef0004460cca87ea0cdf43dff3b3ceb8cb2985bb54d9c
|
Provenance
The following attestation bundles were made for modmex_ai-0.1.0-py3-none-any.whl:
Publisher:
release.yml on modmex/modmex-ai
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
modmex_ai-0.1.0-py3-none-any.whl -
Subject digest:
70cc16ce7d1392991867120250276eed0abd0b3e49ab7be58815d891bf8eabf0 - Sigstore transparency entry: 2215318115
- Sigstore integration time:
-
Permalink:
modmex/modmex-ai@b4685579a417f7d1f6da8757763e0af9c73a63b5 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/modmex
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b4685579a417f7d1f6da8757763e0af9c73a63b5 -
Trigger Event:
push
-
Statement type: