Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ node_modules/
dist/
coverage/
*.log
.venv/
__pycache__/
*.py[cod]
*.egg-info/
build/
.pytest_cache/
85 changes: 85 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,87 @@ pi --model dynamo/<model-id> -p "Reply exactly ok."

For local Dynamo, the API key is usually not checked. This package defaults to `dynamo-local` if `DYNAMO_API_KEY` is unset.


## Generic Agent Proxy

This repository also ships a standalone Python `dynamo-agent-proxy` CLI for
agents that can speak OpenAI or Anthropic APIs but cannot load a Pi plugin. The
proxy is not Pi-specific: it reads `DYNAMO_*` and `DYN_AGENT_*` environment
variables, injects `nvext.agent_context` / `nvext.agent_hints`, and forwards
requests to a Dynamo OpenAI-compatible endpoint. Existing unknown `nvext` fields
are preserved, while proxy-provided context and hint keys take precedence.

Supported client surfaces:

- `POST /v1/chat/completions` - pass-through to Dynamo with `nvext` injection.
- `POST /v1/responses` - pass-through to Dynamo with `nvext` injection.
- `POST /v1/messages` - Anthropic Messages API compatibility, translated to
Dynamo `/v1/chat/completions` and translated back to Anthropic responses.

Install directly from GitHub with `uv`:

```bash
uv pip install "dynamo-agent-proxy @ git+ssh://git@github.com:ai-dynamo/pi-dynamo-provider.git"
```

For local development from a checkout:

```bash
uv pip install -e .
```

Run the proxy:

```bash
export DYNAMO_BASE_URL=http://127.0.0.1:8000/v1
export DYNAMO_API_KEY=dummy
export DYN_AGENT_SESSION_TYPE_ID=generic_agent
export DYN_AGENT_SESSION_ID=agent-demo-001
export DYN_AGENT_TRAJECTORY_ID=agent-demo-001:main

dynamo-agent-proxy --listen-port 18080 --priority 5 --osl 1024
```

Point OpenAI-compatible clients at:

```text
http://127.0.0.1:18080/v1
```

Point Anthropic-compatible clients at:

```text
http://127.0.0.1:18080
```

For example, an Anthropic client will call `POST /v1/messages`; the proxy maps
that request onto Dynamo's OpenAI-compatible `/v1/chat/completions` endpoint.
Tool definitions and tool-choice hints are converted between Anthropic tool
schemas and OpenAI function tools.

Proxy-specific options mirror the environment variables:

```text
--listen-host HOST Bind host. Default: 127.0.0.1
--listen-port PORT Bind port. Default: 18080
--upstream URL Dynamo OpenAI-compatible base URL. Default: http://127.0.0.1:8000/v1
--api-key KEY Bearer token sent to Dynamo. Default: dynamo-local
--model MODEL Fallback model for Anthropic requests without model
--session-type-id VALUE nvext.agent_context.session_type_id. Default: generic_agent
--session-id VALUE nvext.agent_context.session_id. Default: generated proxy id
--trajectory-id VALUE nvext.agent_context.trajectory_id. Default: <session-id>:main
--parent-trajectory-id ID Optional parent trajectory id
--priority INT Convenience hint for nvext.agent_hints.priority
--osl INT Convenience hint for nvext.agent_hints.osl
--agent-hint KEY=VALUE Additional nvext.agent_hints entry; VALUE may be JSON
```

You can also set `DYN_AGENT_HINTS` to a JSON object, for example:

```bash
export DYN_AGENT_HINTS='{"priority":5,"osl":1024}'
```

## Local Dynamo Launcher

For local onboarding, this repo includes two small Dynamo helper scripts.
Expand Down Expand Up @@ -440,6 +521,9 @@ npm install
npm run check
npm run test
npm run build

PYTHONPATH=python python3 -m unittest discover -s test/python
python3 -m pip wheel . --no-deps --no-build-isolation -w /tmp/dynamo-agent-proxy-wheel
```

Run from source without installing:
Expand Down Expand Up @@ -483,6 +567,7 @@ Authentication fails:
Included:

- OpenAI-compatible chat-completions path.
- Generic proxy for OpenAI `/v1/chat/completions`, OpenAI `/v1/responses`, and Anthropic `/v1/messages`.
- Model discovery from `/v1/models`.
- Dynamo request metadata injection.
- Pi session id as default `trajectory_id`.
Expand Down
23 changes: 23 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[build-system]
requires = ["setuptools>=69"]
build-backend = "setuptools.build_meta"

[project]
name = "dynamo-agent-proxy"
version = "0.1.0"
description = "Generic OpenAI and Anthropic compatible proxy that injects Dynamo nvext agent metadata."
readme = "README.md"
requires-python = ">=3.11"
license = { file = "LICENSE" }
authors = [{ name = "NVIDIA" }]
keywords = ["dynamo", "openai", "anthropic", "proxy", "agent", "nvext"]
dependencies = []

[project.scripts]
dynamo-agent-proxy = "dynamo_agent_proxy.proxy:main"

[project.urls]
Repository = "https://github.com/ai-dynamo/pi-dynamo-provider"

[tool.setuptools.packages.find]
where = ["python"]
28 changes: 28 additions & 0 deletions python/dynamo_agent_proxy/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Generic Dynamo agent proxy."""

from .proxy import (
AgentAnnotation,
ProxyConfig,
annotate_json_request_body,
make_handler,
merge_dynamo_metadata,
read_proxy_config,
translate_anthropic_messages_request,
translate_openai_chat_response_to_anthropic,
)

__all__ = [
"AgentAnnotation",
"ProxyConfig",
"annotate_json_request_body",
"make_handler",
"merge_dynamo_metadata",
"read_proxy_config",
"translate_anthropic_messages_request",
"translate_openai_chat_response_to_anthropic",
]

__version__ = "0.1.0"
8 changes: 8 additions & 0 deletions python/dynamo_agent_proxy/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

from .proxy import main


if __name__ == "__main__":
main()
Loading
Loading