Repository navigation
refactor(attribution)!: remove project_id and the LiteLLM cost attribution built on it - #45
Conversation
…ution built on it project_id was the hosted platform's spend key: `agent-env task create` required it, and it became the LiteLLM `user`, a `projectId:` tag, the Modal App name, and a field the A2A validator checked agents forward. agent-env has no project concept, so it is removed rather than generalized. Sandbox cost dimensions stay in the open `attribution` dict that providers already read. - CLI: `task create` no longer takes `--project-id`; `task run` and `run-batch` drop the option and the missing-attribution banner. - Task: no `project_id` field. A stored document that still carries the key loads, and the key is not written back. - LiteLLM: `utils/litellm_attribution.py` is gone. The rubrics judge's direct call sends no `user` or tags, and the auto-deployed judge gets no LITELLM_PROJECT_ID / LITELLM_USER (customer_id) env vars or attribution. - Modal / E2B: one Modal App, the configured base name, instead of one per project; `project_id` is no longer an App tag or E2B metadata key. - Agents: prompt_agent and the judge stop sending `project_id` through agent-config (`task_id` is still sent). The A2A validator drops the static and runtime LiteLLM-attribution checks, and seeds `task_id` with the validation task's own id. - Explorer: `RunRequest.project_id` is gone, so a request that sends it now gets a 422 (the model forbids extra keys); the UI stops sending it. - Tests and the protocol README use neutral attribution keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Where project_id was a sample key beside others, the key is removed rather than swapped for cost_center: the attribution-probe and caller-metadata checks, the e2b attribution test and the chain-forwarding test. The test that cost_center is not a Modal tag, and the assertion that agent-config rejects the key, only existed for project_id and are removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
🤖 Automated Claude routine — Env Pod PR Context Review. This is a bot, not Edgar Arakelyan; not a human review. Offering one piece of context that an in-repo
Because Worth either a line in the break list or a tolerated-and-ignored Generated by Claude Code |
…B metadata Replace the hardcoded product/customer/team App tags and the pipeline_step/run_id-only sandbox tags with one generic _attribution_tags. The shared Modal App is tagged once, so it carries only the deployment's [sandbox.attribution]; each sandbox carries the run's full attribution filled from those defaults. E2B metadata is the same resolved dict. A key Modal can't take raises instead of being rewritten, and the sandbox-started log nests the tags so a key like `name` can't clobber a LogRecord field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Attribution now lands in E2B sandbox metadata next to the provider's own agent_env_exposed_ports key, which reconnect reads back as the sandbox's exposed ports. An attribution value under that key would have been restored as ports that were never exposed, or silently overwritten; raise before resolving a template or creating anything instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
project_idwas the hosted platform's cost-attribution key.agent-env task createrequired it, and it then turned into:userfield and aprojectId:tag on judge callsagent-env has no project concept, so this removes it rather than generalizing it. Sandbox cost dimensions keep flowing through the open
attributiondict (agent_env/attribution.py). That path is now generic too: no key is hardcoded, and providers carry every key.What's removed
agent-env task createno longer takes--project-id(it was required).task runandrun-batchdrop the option and the yellow "LiteLLM cost attribution is incomplete" banner, plus the step-type list that only fed it.Taskhas noproject_idfield. A stored task document that still carries the key loads fine, and the key is not written back (newtst/unit/task/task_document_test.py).utils/litellm_attribution.pyis deleted. Its one caller, the rubrics judge's direct LiteLLM call, now sends nouserormetadata.tags. The helper only produced output when aproject_idwas set, so withproject_idgone it had nothing left to do, which is why I removed it instead of keeping ataskId-only tag. The auto-deployed judge agent no longer getsLITELLM_PROJECT_IDorLITELLM_USER.LITELLM_USERcame fromcontext.metadata["customer_id"], the same platform attribution, which nothing in this repo sets. The judge also no longer gets aproject_id/customerattribution dict.app_name/AGENT_ENV_MODAL_APP_NAME), instead of<base>-<project_id>. Attribution no longer hardcodesproduct/customer/team; one_attribution_tagsreplaces both tag builders:[sandbox.attribution]defaults, under any keys. The App is shared and tagged once, so a run's own values can't go there without billing later runs to the first run's tags.attributiondict (any keys,pipeline_step/run_idincluded), with unset keys filled from those defaults.agent_env_exposed_portsraises aValueErrorbefore anything is created: the provider stores the sandbox's exposed ports under that key and reads it back on reconnect.[a-zA-Z0-9._-]) raises aValueErrorbefore the App is looked up, instead of being rewritten. Values are sanitized as before.modal_sandbox_startedlog line nests the tags undermodal_sandbox_tagsand keepspipeline_step/run_idtop-level for the container join, so a key likenamecan't clobber a LogRecord field.prompt_agentand the rubrics judge stop sendingproject_idthrough/ext/agent-config.task_idis still sent. The test echo agent drops the config field.verify_a2a_litellm_attributionand runtimeverify_a2a_litellm_attribution_runtimechecks are removed. Both checked LiteLLM spend attribution: the runtime one planted a probeproject_idandtask_idand read them back over/ext/attribution-probe. What's left of them, whether an agent passestask_idon to its model gateway, only means something for spend attribution. Agent-config itself stays covered by the identity check, which sets config through the same extension and checks that the served card changes. The validator now seedstask_idwith the validation task's own id.agent-env a2a-agent validatestops printing the twovalidated_litellm_attribution*blocks. The protocol package'surn:agentenv:attribution-probe/v1extension stays: it is an open, key-agnostic map, and only its README example and test values namedproject_id.RunRequest.project_idis removed, and the UI stops sending it: the runner's?projectId=URL parameter, and the Start Runs panel's copy oftask.project_id.Breaking for downstream
Task(..., project_id=...)andTask.put(..., project_id=...)now raiseTypeError, andtask.project_idraisesAttributeError. Callers outside this repo must drop it; in-repo callers are updated here.--project-idontask create,task runandrun-batchnow fails with "No such option". Scripts that pass it must drop it.POST /api/v1/tasks/{id}/runand/runswithproject_idin the body now return 422, becauseRunRequestforbids extra keys.user/projectId:/taskId:tags. A proxy that attributes or gates spend on them no longer gets them from agent-env.[sandbox.attribution]: a run's ownproduct/customer/teammove to the sandbox tags, which Modal's billing report doesn't break down by. An attribution key that isn't a valid Modal tag key now fails the create.project_idand carries every attribution key, includingpipeline_step/run_id. Attribution underagent_env_exposed_portsis refused.project_idthrough agent-config.validated_litellm_attribution*agent metadata keys are gone.check_plugin_api.pyreports no break to the plugin surface. The!marks the CLI and API breaks above.Testing
pytest tst/unit packages/agentenv-protocol/tests -n auto: 5798 passed. The only failures are the 2test_mcp_cli_buildertests that fail identically on main here, because the generated CLI's interpreter has noclick.attribution_wire_test.py), and a tag namednamedoesn't clobber the log record (modal_sandbox_started_log_test.py).npx tsc --noEmit,npm run test:smoke(13 ran, the 2 skips need trajectory fixtures) andnpm run build:staticpass.agent-env task create t.json --id live-checkwith no flag succeeds.--project-id xis rejected ("No such option").agent-env task run --id live-checkscores 1.0, and its context has noproject_id.agent-env run hellopasses.git grep project_idnow finds only GCP's unrelatedquota_project_id(gcs_object_store.pyand its test) and seed data for the mock Jira and Figma services intst/integration/env/gateway/gateway_test.py.🤖 Generated with Claude Code
The PR appears safe to merge; no new issue or outstanding finding remains.
Fix with agent prompt
Summary
This PR removes
project_idas an agent-env cost-attribution field and keeps sandbox attribution as an open map of keys. Tasks, runs, agents, and the rubrics judge stop carrying project IDs; Modal and E2B carry generic attribution on sandboxes instead.project_idfor task runs.Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR Defaults["Configured attribution defaults"] --> App["Shared Modal App tags"] Defaults --> Sandbox["Modal sandbox tags"] Run["Run attribution"] --> Sandbox Defaults --> E2B["E2B sandbox metadata"] Run --> E2BReviews (5) · Last reviewed commit: "fix(e2b): refuse attribution that uses t..."