fix: use platform llm grants for builder
All checks were successful
build / build (push) Successful in 2s

This commit is contained in:
robert
2026-05-26 09:49:40 -03:00
parent ca559b8346
commit 98a8c57c83
4 changed files with 21 additions and 10 deletions

View File

@@ -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` | caller's LLM creds (Card declares `llm_provisioning=caller_provided`) | yes — the LLM bill goes to the caller | | `ctx.llm` | platform-scoped LiteLLM grant (Card declares `llm_provisioning=platform`) | 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,8 @@ marketplace — the dashboard already surfaces these on the agent card.
## Pricing ## Pricing
`$0.10 / call`, plus your LLM cost via your own provider. The deployed `$0.10 / call` during the current pricing model. The deployed
agent is yours forever; subsequent invocations of YOUR agent run on 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

View File

@@ -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`` — caller's LLM creds (this is an expensive skill; 2. ``ctx.llm`` — a platform-scoped LiteLLM grant token for the builder's
the user pays for inference directly via their own provider) 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,15 +53,18 @@ 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.CALLER_PROVIDED llm_provisioning = LLMProvisioning.PLATFORM
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),
) )
pricing = Pricing( pricing = Pricing(
price_per_call_usd=0.10, price_per_call_usd=0.10,
caller_pays_llm=True, caller_pays_llm=False,
notes="LLM cost passes through; deployed agent is yours forever.", notes=(
"Uses a platform-scoped LiteLLM grant for the builder's own "
"reasoning loop; deployed agent is yours forever."
),
) )
@skill( @skill(

View File

@@ -27,8 +27,9 @@ class BuilderContext:
project_prefix: str | None = None project_prefix: str | None = None
workspace: Any | None = None workspace: Any | None = None
grant_token: str | None = None grant_token: str | None = None
# Optional caller-provided LLM creds (when the outer platform Card # LLM creds forwarded by the orchestrator according to this agent's Card.
# declares llm_provisioning=caller_provided). Falls back to settings. # The builder itself uses platform 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

View File

@@ -24,6 +24,13 @@ 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:
agent_source = Path(__file__).resolve().parents[1].joinpath("agent.py").read_text()
self.assertIn("llm_provisioning = LLMProvisioning.PLATFORM", agent_source)
self.assertIn("caller_pays_llm=False", agent_source)
self.assertIn("platform-scoped LiteLLM grant", 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(
"agent_builder/config.py" "agent_builder/config.py"