Multi-Agent Cost Tracking
Track an orchestrator and its sub-agents with one project key, inherited tags, and agent-run cost grouping
Use one Solwyn project key for a multi-agent workflow, then give each agent its own nested run. The run hierarchy answers which agent spent it? while tags answer cross-cutting questions such as which environment, workflow, or customer tier drove it?
Before starting, export SOLWYN_API_KEY and SOLWYN_PROJECT_ID for the same project. SOLWYN_PROJECT_ID is the project id paired with that key; copy it from the dashboard or a project API response.
import os
import solwyn
from openai import OpenAI
client = solwyn.Solwyn(
OpenAI(),
api_key=os.environ["SOLWYN_API_KEY"],
tags={"environment": "production", "service_name": "content_pipeline"},
)
with solwyn.run(
"article_orchestrator",
tags={"workflow": "quantum_article"},
) as orchestrator_run_id:
brief = client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "user",
"content": "Create a research brief for a quantum-computing article.",
}
],
)
with solwyn.run("research_agent", tags={"agent_name": "research"}) as research_run_id:
research = client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "user",
"content": f"Research this brief: {brief.choices[0].message.content}",
}
],
)
with solwyn.run("writing_agent", tags={"agent_name": "writing"}) as writing_run_id:
draft = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "You are a technical writer."},
{
"role": "user",
"content": f"Write an article based on: {research.choices[0].message.content}",
},
],
)
with solwyn.run("review_agent", tags={"agent_name": "review"}) as review_run_id:
review = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "Review this draft for technical accuracy."},
{"role": "user", "content": draft.choices[0].message.content},
],
)
print(
orchestrator_run_id,
research_run_id,
writing_run_id,
review_run_id,
)
print(review.choices[0].message.content)
client.close()Every child run inherits workflow=quantum_article from the orchestrator (SDK v0.5.0+), and the client's environment and service_name defaults are applied when each call is captured. A child's agent_name identifies its role without repeating the shared attribution. Each child also records the orchestrator as its parent_agent_run_id, so the run listing and the dashboard's Agents tab can reconstruct the direct tree.
Client-wide tags= is the simplest way to stamp every call, and it has one operational consequence: a tagged call always takes a live budget check, bypassing the allow cache and the run lease, because a tag-scoped budget needs the tag snapshot to decide. If your agents make many small calls inside a run and you want the lease path, move the shared tags onto the orchestrator's solwyn.run(...) instead — nested runs inherit them — and leave the client untagged. See Tags and budget admission.
Spend per agent in one call
Query costs with group_by=agent_run to get cost, tokens, call count, and the attached agent_run_name for each run. Add tag filters when you want one slice of the shared project:
curl --get \
"https://api.solwyn.ai/api/v1/projects/${SOLWYN_PROJECT_ID}/costs" \
--header "Authorization: Bearer ${SOLWYN_API_KEY}" \
--data-urlencode "range=30d" \
--data-urlencode "group_by=agent_run" \
--data-urlencode "tag.environment=production"The response's group_key is the stored run id and agent_run_name is the name passed to solwyn.run(...). Results are ordered by cost, include at most 100 groups, and set truncated: true when more groups matched. Run and tag grouping are available on Team and Scale.
Discover the tags in use
You do not need to remember every key spelling. Discover active keys, their distinct-value counts, last-seen timestamps, likely case/whitespace variants, and account cap usage:
curl --get \
"https://api.solwyn.ai/api/v1/projects/${SOLWYN_PROJECT_ID}/tags" \
--header "Authorization: Bearer ${SOLWYN_API_KEY}"Then list values for one key, optionally narrowed by a case-sensitive prefix:
curl --get \
"https://api.solwyn.ai/api/v1/projects/${SOLWYN_PROJECT_ID}/tags/values" \
--header "Authorization: Bearer ${SOLWYN_API_KEY}" \
--data-urlencode "key=agent_name" \
--data-urlencode "q=research"Prefer consistent lowercase_snake_case keys such as agent_name, customer_tier, and workflow. Solwyn preserves tag keys verbatim; it does not normalize case or whitespace.
Stopping a sub-agent that runs away
Because each agent has its own run id, each one can be stopped on its own. Open the project's Agents tab, find research_agent, and press Stop: the SDK's next call inside that scope raises RunStoppedError — not a BudgetExceededError, so a budget handler cannot retry through it — while the orchestrator and the other agents continue. The SDK's local velocity detector flags a looping agent the same way, per run. See Run control.
Choosing attribution and budget boundaries
Agent runs and tags are complementary within one project key:
| Need | Use |
|---|---|
| Cost and latency for one execution | A distinct solwyn.run(...) scope |
| Orchestrator → sub-agent hierarchy | Nested runs |
| Spend by role across many executions | A stable tag such as agent_name |
| A ceiling for one execution | An agent_run-scoped budget using the raw run id |
| A ceiling for every execution | A runaway-run rule |
| A ceiling across matching work | A tag-scoped budget, such as customer_tier=enterprise |
| Ending one execution now | Stop on the Agents tab |
All of these share the project's overall budget. Use separate project keys only when the workloads are genuinely separate projects that need independently administered keys, dashboards, and project-level budgets.
If your agents run under a framework — OpenAI Agents, LangChain, CrewAI — the framework integrations create these runs for you from the framework's own lifecycle callbacks.
See Agent Runs for inheritance, inherit_tags=False, async propagation, current_run_context(), and detached run handles. See Reading cost views for cost filters, discovery limits, raw and stored run ids, and tag-scoped budget fields.