feat: let builder use caller or platform llm
All checks were successful
build / build (push) Successful in 23s
All checks were successful
build / build (push) Successful in 23s
This commit is contained in:
@@ -47,7 +47,7 @@ Three platform-managed bits land on the inner skill via `RunContext`:
|
|||||||
| Field | Source | Required? |
|
| Field | Source | Required? |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `ctx.workspace.bucket` | grant minted by the orchestrator | yes |
|
| `ctx.workspace.bucket` | grant minted by the orchestrator | yes |
|
||||||
| `ctx.llm` | platform-scoped LiteLLM grant (Card declares `llm_provisioning=platform`) | yes |
|
| `ctx.llm` | caller-selected LLM creds when available, otherwise a platform-scoped LiteLLM grant (Card declares `llm_provisioning=platform_or_caller_provided`) | yes |
|
||||||
| `ctx.cp_jwt` | caller's CP JWT (Card declares `wants_cp_jwt=True`) | yes — used by `cp_deploy_tarball` |
|
| `ctx.cp_jwt` | caller's CP JWT (Card declares `wants_cp_jwt=True`) | yes — used by `cp_deploy_tarball` |
|
||||||
|
|
||||||
The user opts into all three when they pick `agent-builder` from the
|
The user opts into all three when they pick `agent-builder` from the
|
||||||
@@ -55,8 +55,9 @@ marketplace — the dashboard already surfaces these on the agent card.
|
|||||||
|
|
||||||
## Pricing
|
## Pricing
|
||||||
|
|
||||||
`$0.10 / call` during the current pricing model. The deployed
|
`$0.10 / call` during the current pricing model. The builder uses your
|
||||||
agent is yours forever; subsequent invocations of your agent run on
|
selected LLM creds when present and falls back to a scoped platform grant.
|
||||||
|
The deployed agent is yours forever; subsequent invocations of your agent run on
|
||||||
their own pricing (whatever you set when generating it).
|
their own pricing (whatever you set when generating it).
|
||||||
|
|
||||||
## How to call it
|
## How to call it
|
||||||
|
|||||||
11
agent.py
11
agent.py
@@ -10,8 +10,8 @@ This agent only works when invoked through the platform orchestrator,
|
|||||||
because it needs three things forwarded from the caller:
|
because it needs three things forwarded from the caller:
|
||||||
|
|
||||||
1. ``ctx.workspace.bucket`` — the user's MinIO bucket
|
1. ``ctx.workspace.bucket`` — the user's MinIO bucket
|
||||||
2. ``ctx.llm`` — a platform-scoped LiteLLM grant token for the builder's
|
2. ``ctx.llm`` — caller-selected LLM credentials when available, otherwise
|
||||||
own DeepAgents loop
|
a platform-scoped LiteLLM grant token for the builder's own DeepAgents loop
|
||||||
3. ``ctx.cp_jwt`` — the user's CP JWT, so cp_deploy_tarball can
|
3. ``ctx.cp_jwt`` — the user's CP JWT, so cp_deploy_tarball can
|
||||||
POST to /v1/agents/from-tarball as the user.
|
POST to /v1/agents/from-tarball as the user.
|
||||||
"""
|
"""
|
||||||
@@ -53,7 +53,7 @@ class AgentBuilder(A2AAgent[BuilderConfig, NoAuth]):
|
|||||||
config_model = BuilderConfig
|
config_model = BuilderConfig
|
||||||
auth_model = NoAuth
|
auth_model = NoAuth
|
||||||
tools_used = ("deepagents", "langgraph", "a2a-pack", "litellm", "microsandbox")
|
tools_used = ("deepagents", "langgraph", "a2a-pack", "litellm", "microsandbox")
|
||||||
llm_provisioning = LLMProvisioning.PLATFORM
|
llm_provisioning = LLMProvisioning.PLATFORM_OR_CALLER_PROVIDED
|
||||||
wants_cp_jwt = True
|
wants_cp_jwt = True
|
||||||
workspace_access = WorkspaceAccess.dynamic(
|
workspace_access = WorkspaceAccess.dynamic(
|
||||||
allowed_modes=(WorkspaceMode.READ_ONLY, WorkspaceMode.READ_WRITE_OVERLAY),
|
allowed_modes=(WorkspaceMode.READ_ONLY, WorkspaceMode.READ_WRITE_OVERLAY),
|
||||||
@@ -62,8 +62,9 @@ class AgentBuilder(A2AAgent[BuilderConfig, NoAuth]):
|
|||||||
price_per_call_usd=0.10,
|
price_per_call_usd=0.10,
|
||||||
caller_pays_llm=False,
|
caller_pays_llm=False,
|
||||||
notes=(
|
notes=(
|
||||||
"Uses a platform-scoped LiteLLM grant for the builder's own "
|
"Uses caller-selected LLM credentials when available; otherwise "
|
||||||
"reasoning loop; deployed agent is yours forever."
|
"uses a platform-scoped LiteLLM grant for the builder's own "
|
||||||
|
"reasoning loop. Deployed agents remain yours."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ class BuilderContext:
|
|||||||
workspace: Any | None = None
|
workspace: Any | None = None
|
||||||
grant_token: str | None = None
|
grant_token: str | None = None
|
||||||
# LLM creds forwarded by the orchestrator according to this agent's Card.
|
# LLM creds forwarded by the orchestrator according to this agent's Card.
|
||||||
# The builder itself uses platform grants; generated agents usually use
|
# The builder accepts caller-selected creds and falls back to platform
|
||||||
# caller-provided creds. Falls back to settings for local development.
|
# grants; generated agents usually use caller-provided creds. Falls back
|
||||||
|
# to settings for local development.
|
||||||
llm_base_url: str | None = None
|
llm_base_url: str | None = None
|
||||||
llm_api_key: str | None = None
|
llm_api_key: str | None = None
|
||||||
llm_model: str | None = None
|
llm_model: str | None = None
|
||||||
@@ -57,6 +58,10 @@ Default generated/user agents to ``LLMProvisioning.CALLER_PROVIDED`` and
|
|||||||
usage. ``LLMProvisioning.PLATFORM`` is allowed only through the A2A LiteLLM
|
usage. ``LLMProvisioning.PLATFORM`` is allowed only through the A2A LiteLLM
|
||||||
grant path: the generated agent must still read ``ctx.llm`` and must never read
|
grant path: the generated agent must still read ``ctx.llm`` and must never read
|
||||||
``A2A_LITELLM_KEY`` or any provider key directly.
|
``A2A_LITELLM_KEY`` or any provider key directly.
|
||||||
|
``LLMProvisioning.PLATFORM_OR_CALLER_PROVIDED`` is reserved for trusted
|
||||||
|
platform/meta agents that should prefer caller-selected credentials and fall
|
||||||
|
back to a scoped platform grant; do not use it for ordinary generated agents
|
||||||
|
unless the user explicitly asks for that mixed billing mode.
|
||||||
|
|
||||||
You have packaged DeepAgents skills loaded from ``/.agent-builder/skills/``.
|
You have packaged DeepAgents skills loaded from ``/.agent-builder/skills/``.
|
||||||
Use ``deepagent-agent-design`` before designing generated agent internals,
|
Use ``deepagent-agent-design`` before designing generated agent internals,
|
||||||
|
|||||||
@@ -24,8 +24,10 @@ Use `A2AAgent` class attributes for runtime and marketplace behavior:
|
|||||||
For generated/user-owned agents that call an LLM, default to
|
For generated/user-owned agents that call an LLM, default to
|
||||||
`LLMProvisioning.CALLER_PROVIDED` with `Pricing(..., caller_pays_llm=True, ...)`.
|
`LLMProvisioning.CALLER_PROVIDED` with `Pricing(..., caller_pays_llm=True, ...)`.
|
||||||
Use `LLMProvisioning.PLATFORM` only when the user explicitly wants
|
Use `LLMProvisioning.PLATFORM` only when the user explicitly wants
|
||||||
platform-paid LLM usage. In either mode, read `ctx.llm`; never read
|
platform-paid LLM usage. Reserve `LLMProvisioning.PLATFORM_OR_CALLER_PROVIDED`
|
||||||
`A2A_LITELLM_KEY`, provider keys, or platform secrets directly.
|
for trusted platform/meta agents that must work with either the caller's
|
||||||
|
selected creds or a platform fallback grant. In every mode, read `ctx.llm`;
|
||||||
|
never read `A2A_LITELLM_KEY`, provider keys, or platform secrets directly.
|
||||||
|
|
||||||
Keep each `@skill` method `async`, put `RunContext[...]` immediately after
|
Keep each `@skill` method `async`, put `RunContext[...]` immediately after
|
||||||
`self`, and annotate every public argument. Do not use `*args` or `**kwargs`;
|
`self`, and annotate every public argument. Do not use `*args` or `**kwargs`;
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ Check these items:
|
|||||||
- The public schema is small and stable. No unbounded command strings, hidden
|
- The public schema is small and stable. No unbounded command strings, hidden
|
||||||
prompt fragments, or provider credentials as public inputs.
|
prompt fragments, or provider credentials as public inputs.
|
||||||
- The implementation reads `ctx.llm` for both `CALLER_PROVIDED` and
|
- The implementation reads `ctx.llm` for both `CALLER_PROVIDED` and
|
||||||
`PLATFORM` LLM provisioning.
|
`PLATFORM` LLM provisioning. `PLATFORM_OR_CALLER_PROVIDED` is acceptable
|
||||||
|
only for trusted platform/meta agents and must also read `ctx.llm`.
|
||||||
- Generated/user agents should default to
|
- Generated/user agents should default to
|
||||||
`llm_provisioning = LLMProvisioning.CALLER_PROVIDED` and
|
`llm_provisioning = LLMProvisioning.CALLER_PROVIDED` and
|
||||||
`Pricing(..., caller_pays_llm=True, ...)`. If the user explicitly wants
|
`Pricing(..., caller_pays_llm=True, ...)`. If the user explicitly wants
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ if TYPE_CHECKING:
|
|||||||
from .config import Settings
|
from .config import Settings
|
||||||
|
|
||||||
|
|
||||||
A2A_PACK_MIN_VERSION = "0.1.27"
|
A2A_PACK_MIN_VERSION = "0.1.28"
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
a2a-pack>=0.1.27
|
a2a-pack>=0.1.28
|
||||||
httpx>=0.27
|
httpx>=0.27
|
||||||
boto3>=1.34
|
boto3>=1.34
|
||||||
deepagents>=0.5.0
|
deepagents>=0.5.0
|
||||||
|
|||||||
@@ -24,12 +24,16 @@ class BuilderPromptTests(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
self.assertIn("stream=True", agent_source)
|
self.assertIn("stream=True", agent_source)
|
||||||
|
|
||||||
def test_outer_builder_uses_platform_llm_grants(self) -> None:
|
def test_outer_builder_accepts_user_creds_or_platform_llm_grants(self) -> None:
|
||||||
agent_source = Path(__file__).resolve().parents[1].joinpath("agent.py").read_text()
|
agent_source = Path(__file__).resolve().parents[1].joinpath("agent.py").read_text()
|
||||||
|
|
||||||
self.assertIn("llm_provisioning = LLMProvisioning.PLATFORM", agent_source)
|
self.assertIn(
|
||||||
|
"llm_provisioning = LLMProvisioning.PLATFORM_OR_CALLER_PROVIDED",
|
||||||
|
agent_source,
|
||||||
|
)
|
||||||
self.assertIn("caller_pays_llm=False", agent_source)
|
self.assertIn("caller_pays_llm=False", agent_source)
|
||||||
self.assertIn("platform-scoped LiteLLM grant", agent_source)
|
self.assertIn("platform-scoped LiteLLM grant", agent_source)
|
||||||
|
self.assertIn("caller-selected LLM credentials", agent_source)
|
||||||
|
|
||||||
def test_builder_defaults_raise_timeout_budget(self) -> None:
|
def test_builder_defaults_raise_timeout_budget(self) -> None:
|
||||||
config_source = Path(__file__).resolve().parents[1].joinpath(
|
config_source = Path(__file__).resolve().parents[1].joinpath(
|
||||||
|
|||||||
Reference in New Issue
Block a user