Repository navigation
[bot] Add Llama Stack Client Python SDK integration for inference, agents, and responses execution instrumentation (16,527 weekly downloads) #535
Description
Summary
The Llama Stack Client Python SDK (llama-stack-client) is Meta's official Python client for Llama Stack — a production framework for building LLM-powered applications using Llama models. Latest stable release: v0.2.x / 0.6.0 (March 2026), actively maintained by the llamastack organization. This repository has zero instrumentation for any llama-stack-client execution surface — no integration directory, no wrapper, no patcher, no auto_instrument() support.
Llama Stack provides standardized inference, safety, agents, and retrieval APIs that work uniformly across local (Ollama, vLLM, TGI), cloud (Together AI, Fireworks, SambaNova), and Meta's own managed endpoints. Its LlamaStackClient and AsyncLlamaStackClient expose a distinct execution API (client.inference.chat_completion(), client.agents.*, client.responses.create()) that is separate from the OpenAI SDK shape and is not coverable by wrap_openai().
Comparable provider and agent framework integrations in this repo: openai, anthropic, mistral, cohere, huggingface-hub, openai-agents, claude-agent-sdk, google-adk.
What needs to be instrumented
The llama-stack-client package exposes these execution surfaces, none of which are instrumented:
Inference API (highest priority)
| SDK Method | Description | Return type |
|---|---|---|
client.inference.chat_completion(model_id, messages, ...) |
Chat completions with Llama models — supports tool calling, streaming, structured output | ChatCompletionResponse or AsyncIterator[ChatCompletionStreamChunk] |
client.inference.completion(model_id, content, ...) |
Text completions from a prompt | CompletionResponse |
client.inference.embeddings(model_id, contents, ...) |
Text embeddings generation | EmbeddingsResponse |
ChatCompletionResponse includes completion_message (content + tool calls), logprobs, and usage metadata (prompt_tokens, completion_tokens, total_tokens). Streaming yields ChatCompletionStreamChunk objects with delta content.
Responses API (agent execution)
| SDK Method | Description | Return type |
|---|---|---|
client.responses.create(input, model, tools=..., ...) |
Agentic response generation — automatically handles tool calls, knowledge bases, and conversation state | OpenAIResponseObject |
The Responses API is Llama Stack's high-level agent execution surface introduced in early 2026. It accepts a conversation thread, configured tools (MCP servers, function calls, web search), and returns a completed response after autonomously handling tool invocations in a loop.
Agents API (lower-level agent runs)
| SDK Method | Description | Return type |
|---|---|---|
client.agents.create(agent_config) |
Create an agent with a specific system prompt, tools, and model | AgentCreateResponse |
client.agents.sessions.create(agent_id, ...) |
Create a session for multi-turn conversations | Session |
client.agents.turns.create(agent_id, session_id, messages, ...) |
Execute an agent turn, yielding events (tool calls, completions, etc.) | AgentTurnResponseStreamChunk iterator |
The Agents API provides a lower-level interface for multi-turn agent execution with explicit turn management. Each turn involves an LLM call, optional tool execution, and a final completion.
Async variants
All methods have async equivalents on AsyncLlamaStackClient with identical signatures.
Implementation notes
Distinct client from OpenAI: LlamaStackClient is a Stainless-generated client (like Groq, Mistral, Together) but with a Llama Stack-specific API shape. It is not a subclass of openai.OpenAI and cannot be wrapped with wrap_openai().
Provider-agnostic: Llama Stack routes inference to any configured backend (Ollama, Together, Fireworks, SambaNova, Meta's managed endpoints). The integration should capture the model_id and, where available, the backend provider from response metadata.
Streaming: Both inference.chat_completion() and agents.turns.create() support streaming. The integration must handle streaming span lifecycle (start span on call, accumulate chunks/events, finalize on exhaustion).
Tool call tracing: When agents invoke tools, the turn response stream includes AgentTurnResponseStepStartPayload and AgentTurnResponseStepCompletePayload events for each tool call. These should be captured as child spans of the agent turn span.
Token usage: ChatCompletionResponse includes a usage field with prompt_tokens, completion_tokens, total_tokens — directly usable for span metrics.
No coverage in any instrumentation layer
- No integration directory (
py/src/braintrust/integrations/llama_stack/) - No wrapper function (e.g.
wrap_llama_stack()) - No patcher in any existing integration
- No nox test session (
test_llama_stack) - No version entry in
py/src/braintrust/integrations/versioning.py - No mention in
py/src/braintrust/integrations/__init__.py - No entry in
[tool.braintrust.matrix]inpy/pyproject.toml
A grep for llama_stack, llama-stack, llamastack across py/src/braintrust/ returns zero matches.
Braintrust docs status
not_found — llama-stack-client is not listed on the Braintrust integrations directory or the tracing guide. No auto_instrument() reference and no wrap_llama_stack() function are documented anywhere in Braintrust docs.
Upstream references
- llama-stack-client on PyPI: https://pypi.org/project/llama-stack-client/
- llama-stack-client-python on GitHub: https://github.com/llamastack/llama-stack-client-python
- Llama Stack main project: https://github.com/meta-llama/llama-stack
- Llama Stack inference API docs: https://llamastack.github.io/docs/references/python-sdk-reference/inference
- Llama Stack agents API docs: https://llamastack.github.io/docs/references/python-sdk-reference/agents
- Responses API article: https://developers.redhat.com/articles/2026/03/09/automate-ai-agents-responses-api-llama-stack
- DataCamp Llama Stack guide: https://www.datacamp.com/tutorial/llama-stack
Local repo files inspected
py/src/braintrust/integrations/— nollama_stack/directory onmainpy/src/braintrust/wrappers/— no Llama Stack wrapperpy/noxfile.py— notest_llama_stacksessionpy/pyproject.toml[tool.braintrust.matrix]— no llama-stack-client entrypy/src/braintrust/integrations/__init__.py— Llama Stack not listedpy/src/braintrust/integrations/versioning.py— no Llama Stack version matrix- Full repo grep for
llama_stack,llama-stack,llamastack— zero matches in SDK source
Activity
[-][bot] Add Llama Stack Client Python SDK integration for inference, agents, and responses execution instrumentation[/-][+][bot] Add Llama Stack Client Python SDK integration for inference, agents, and responses execution instrumentation (16,527 weekly downloads)[/+]AbhiPrasad commentedon Aug 10, 2026
Spec compliance review
Automated review of the requirements stated in this issue against the Braintrust instrumentation specification (
braintrust-spec→skills/instrumentation-spec/references/instrumentation-guide.mdandreferences/features/). This reviews the issue text, not any implementation.Verdict: NEEDS REVISION — and, more urgently, the API surface this issue specifies is stale and mostly does not exist in the current
llama-stack-client.Category: completion API + agentic framework + embeddings (mixed; all OpenAI-compatible surfaces)
Already implemented in this repo: no — there is no
py/src/braintrust/integrations/llama_stack/, no wrapper, no patcher, no[tool.braintrust.matrix]entry, and no nox session. A repo grep forllama_stack/llama-stack/llamastackreturns zero hits in SDK source. (Thellama-index-coreentries atpy/pyproject.toml:460-462are a different package and unrelated.)I installed the package to verify this issue's claims rather than taking the tables at face value:
I then enumerated the resource tree at
llama_stack_client/resources/and the client properties inllama_stack_client/_client.py. Results:llama-stack-client0.7.5?client.inference.chat_completion(...)client.inferenceresource at all.client.inference.completion(...)client.inference.embeddings(...)client.agents.create(...)client.agentsresource exists.client.agents.sessions.create(...)client.agents.turns.create(...)AgentTurnResponseStreamChunk, noAgentTurnResponseStepStartPayload/AgentTurnResponseStepCompletePayload.client.responses.create(...)The only
inferenceresource isclient.alpha.inference(llama_stack_client/resources/alpha/inference.py), and it exposes exactly one method:rerank().The surfaces that actually exist today are:
client.chat.completions.create()POST /v1/chat/completionsclient.completions.create()POST /v1/completionsclient.embeddings.create()POST /v1/embeddingsclient.responses.create()POST /v1/responsesclient.alpha.inference.rerank()Also present and unaddressed by this issue:
client.conversations,client.moderations,client.safety,client.shields,client.vector_io,client.vector_stores,client.batches,client.files,client.prompts.Two consequences:
The version claim is wrong. The issue says:
That is both internally inconsistent and stale. The current release is 0.7.5. The
inference/agentsAPI this issue describes is the pre-0.3 Llama Stack shape, which has since been replaced by OpenAI-compatible routes.Most of the "What needs to be instrumented" section needs to be rewritten, not merely corrected — the Inference API table and the entire Agents API section describe methods that cannot be patched because they do not exist.
Conflicts with the spec
1. "Separate from the OpenAI SDK shape" is incorrect, and it drives the wrong normalization conclusion
The issue states:
The class-identity half is true. The payload half is not. In 0.7.5 the payloads are explicitly OpenAI-compatible — the docstring on
client.chat.completions.createliterally reads "Generate an OpenAI-compatible chat completion for the given messages using the specified model." The request takes OpenAImessages,tools,tool_choice,parallel_tool_calls,response_format, and the response typeCompletionCreateResponse(llama_stack_client/types/chat/completion_create_response.py) is:{id, choices[], created, model, usage}choices[].finish_reason: Literal["stop", "length", "tool_calls", "content_filter", "function_call"]choices[].message.tool_calls[].function.arguments: str— already a JSON-encoded stringchoices[].message.tool_calls[].type: Literal["function"], withid: strSpec rule:
instrumentation-guide.md§Canonical payload format — Braintrust ships built-in UI normalizers only for OpenAI, Anthropic, and Google. Llama Stack is none of these, so "instrumentation MUST convert payloads into the OpenAI Chat Completions format so that the UI can render them." There is no "preserve native payload" allowance here.Correction: State the requirement explicitly —
inputMUST be an OpenAI-style ordered messages array (§Input capture) andoutputMUST be an array of OpenAI-style choice objects (§Output capture) — and note that forchat.completionsthis is effectively a passthrough rather than a bespoke translation layer. Do not let the "distinct client" framing imply that a Llama-Stack-native payload may be logged as-is.2.
total_tokensproposed as a span metric"Directly usable" is wrong for the third one.
total_tokensis not a Braintrust metric key.Spec rules:
instrumentation-guide.md§Metrics Reference: "SDKs MUST NOT add metrics beyond the keys listed in this guide." The canonical total istokens.features/token-and-cost-metrics.md§Canonical metrics: "input_tokens,output_tokens, andtotal_tokensare accepted by some OpenTelemetry ingestion adapters and normalized toprompt_tokens,completion_tokens, andtokens. SDKs emitting Braintrust-native metrics MUST use the canonical Braintrust names directly."Correction: map
usage.total_tokens→metrics.tokens. (Prior art already does exactly this — see the_TOKEN_NAME_MAPentry"total_tokens": "tokens"inpy/src/braintrust/integrations/mistral/tracing.py.)3.
metadata.providertreated as optionalSpec rules:
instrumentation-guide.md§Metadata — "Every LLM span MUST include" bothmodelandprovider, and "Theproviderfield is required even when model names are globally recognizable, because gateways and resellers can price the same model differently."features/token-and-cost-metrics.md§Span identity marksmetadata.provideras MUST.Correction:
metadata.providermust be emitted unconditionally. A fixed"llama-stack"is the defensible value, since Llama Stack is precisely the gateway whose routing determines which pricing surface applies — this is the case the spec's "provider, gateway, or reseller" wording exists for. Separately,metadata.modelSHOULD come from the response'smodelfield, not the requestmodel_id: §Metadata says "Themodelfield SHOULD use the model string from the API response (which may include a version suffix) rather than the string the user passed in the request."4. Control-plane calls proposed as instrumentation targets
These are resource-registration calls, not model calls.
instrumentation-guide.md§Span types defines no span type that fits them (llm,function,task,tool,eval,score,automation,preprocessor,classifier,review), and §Data capturing policy states "Instrumentation MUST NOT capture any data or fields unless this guide explicitly requires or allows them."Correction: drop them. (Independently moot — the resource does not exist.)
5.
logprobslisted among the response data to captureSpec rule: §Data capturing policy — "Instrumentation MUST NOT capture any data or fields unless this guide explicitly requires or allows them. This applies even when the backend can store arbitrary JSON or a provider/framework SDK exposes additional data." Neither
logprobsnortop_logprobsis sanctioned anywhere in the guide.Correction: state explicitly that
choices[].logprobsis excluded from bothoutputandmetadata.6. The Responses API is treated as unconditionally agentic
Spec rule:
instrumentation-guide.md§Completion APIs vs Agentic APIs lists "OpenAI Responses API (with tools)" as the agentic example. Aresponses.createcall with notoolsruns no internal tool loop and is a single request-response — onellmspan, no children.Correction: gate the span-tree shape on the presence of
tools: with tools → the agentic tree; without tools → exactly onellmspan per §One span per API call.Missing requirements
The issue is written as a discovery/gap report, so it never states most of what the spec mandates. Each of the following must be added.
1. Span structure — the issue never names a single span type. Required:
chat.completions.create,completions.create,embeddings.create→ exactly onellmspan per API call (§One span per API call: "A streaming call produces a single span that closes when the stream finishes, not one span per chunk.").responses.createwith tools → §Agentic API Instrumentation: one parenttaskspan (span_attributes.type = "task") wrapping childllmspans and childtoolspans.2. Tool-call payload shape. §Tool calls in completion API output requires, for any provider without a UI normalizer:
id(MUST),type: "function"(MUST),function.name(MUST), andfunction.argumentsas a JSON-encoded string (MUST) — "not a JSON object … The string MAY be malformed JSON if the model produced invalid output — in which case it MUST be passed through as-is rather than dropped." Free forchat.completions(alreadystr), but Responses-APIfunction_calloutput items usecall_id+argumentsand need explicit mapping to the OpenAI tool-call object.3.
metadata.toolsand tool request controls. §Available tool definitions: "Available tool definitions MUST be placed inmetadata.toolson everyllmspan whose request makes one or more tools available", as OpenAI-shaped tool objects; omit entirely rather than emitting[]; and "If a model call occurs inside an agentic tool-use loop, each childllmspan MUST include themetadata.toolsvalue for the tools available to that specific model call." Also required when supplied:metadata.tool_choice(normalized to"auto"/"none"/"required"/{type:"function",...}),metadata.parallel_tool_calls,metadata.max_tool_calls. All oftools,tool_choice,parallel_tool_calls,max_tool_callsare real parameters onclient.responses.create;tools,tool_choice,parallel_tool_callsare real onclient.chat.completions.create. Per §Provider-native tool types, the built-in Responses tool types (web_search,file_search,mcp,code_interpreter) MUST keep their provider-nativetypeand MUST NOT be given fake function names or schemas.4. A metadata allowlist — this is the largest omission. §Metadata permits only
model,provider, and "temperature,top_p,max_tokens,frequency_penalty,presence_penalty,stop, andresponse_format", plus the tool fields above andmetadata.prompt, closing with "Any additional metadata field requires a specification update before SDKs emit it."The issue names no allowlist, which leaves an implementer free to emit unsanctioned keys. Concretely not sanctioned and MUST NOT be emitted:
agent_id,session_id,turn_id(all three appear in this issue's own method signatures), and the real 0.7.5 request paramsconversation,previous_response_id,guardrails,max_infer_iters,safety_identifier,prompt_cache_key,service_tier,seed,user,n,logit_bias,logprobs,top_logprobs,reasoning_effort,include,background,store, plus the response envelope fieldsid,object,created.5.
time_to_first_token. The issue discusses streaming lifecycle:…which correctly matches §Streaming → Accumulation, but omits the metric. §Streaming → Additional metrics: "This metric MUST be captured for all streaming calls. It is measured by the SDK, not reported by the provider."
features/token-and-cost-metrics.mdlists it as MUST for streaming spans.6. Reasoning and cache metrics.
CompletionCreateResponse.Usagecarriescompletion_tokens_details.reasoning_tokensandprompt_tokens_details.cached_tokens. Required mappings:completion_tokens_details.reasoning_tokens→metrics.completion_reasoning_tokens(§Reasoning Models: MUST when the provider reports it)prompt_tokens_details.cached_tokens→metrics.prompt_cached_tokens(§Token Caching: SHOULD when reported)Per
features/token-and-cost-metrics.md§Canonical metrics: "Omit a metric when the provider did not report it and the SDK cannot compute it accurately. Do not fabricate zero values to hide missing provider data." Note every field onUsageisOptional[int], so omission handling is load-bearing here.7. Embeddings are entirely unspecified. The issue lists the surface and says only "Text embeddings generation".
features/embeddings.mdis prescriptive and none of it appears in this issue:llmspan, including a request that contains multiple input items. Instrumentation MUST NOT emit one span per returned embedding." Plusmetadata.providerandmetadata.model.{ inputs: [{ content: string | EmbeddingContentPart[] }], output_dimensions?: number }. The realclient.embeddings.createhas adimensionsparam → map tooutput_dimensions. "Other provider configuration fields MUST NOT be captured until added to this specification" — soencoding_formatanduserare excluded.{ count: number }and nothing else.CreateEmbeddingsResponse.data[].embeddingisUnion[List[float], str](float array or base64) and MUST NOT be logged: "Instrumentation MUST NOT capture: raw vector values, prefixes or samples of vector values, vector hashes, vector dimensions, including the actual returned dimension, vector norms or other derived vector statistics." Also "Provider-native embedding responses MUST NOT be logged."prompt_tokensandtokensonly when reported; "completion_tokens… MUST be omitted. Instrumentation MUST NOT fabricatecompletion_tokens: 0." This directly contradicts the issue's blanket "usage … directly usable for span metrics" line, sinceCreateEmbeddingsResponse.Usageexposesprompt_tokensandtotal_tokens.8. Error handling. Not mentioned anywhere in the issue. Provider failures MUST populate the span's top-level
errorfield. For embeddings specifically,features/embeddings.md§Errors and partial responses: "If a provider returns partial batch results with an error,countMUST equal the number actually returned", and "instrumentation MUST NOT log provider-native output while reporting an error."9.
context.span_originprovenance. §Context: "Braintrust-created spans MUST also include span-origin provenance." Repo convention is a module-level instrumentation constant threaded through a localstart_spanshim — seepy/src/braintrust/integrations/mistral/tracing.py:22-29:The issue should require the equivalent
_INSTRUMENTATION = "llama-stack-auto".10. Span naming. §Span naming requires names that reflect provider and operation; the issue specifies none. Repo convention is dotted paths (e.g.
openai.responses.createatopenai/tracing.py:1138), sollama_stack.chat.completions.createand similar.11. Multimodal / attachments. Not mentioned. Both
chat.completionsmessages andresponses.createinput accept media parts —response_create_params.pydefinesinput_imageandinput_filecontent types. §Multimodal / Attachments: "SDKs MUST replace the raw media with Braintrust attachment references inside the log row'sinputoroutputpayload", and "If conversion or upload fails, instrumentation MUST preserve the original payload and MUST NOT throw an exception that prevents span export."12. Parent-span aggregation for agentic runs. §Parent span: the
taskspan SHOULD capture the initial input, the final response, and "Aggregated token counts across all child LLM calls (tokens,prompt_tokens,completion_tokens)".13. Child-span ordering. §Ordering: "Child spans MUST reflect the actual execution order."
14. Repo wiring specifics. The issue correctly observes these are absent but does not state them as requirements. Per
CLAUDE.md: a[tool.braintrust.matrix.llama-stack-client]block with an explicitlatestpin, a[tool.braintrust.cassette-dirs]entry (required for the stale-cassette pre-commit check), atest_llama_stacknox session inpy/noxfile.py, inclusion in.github/workflows/checks.yaml, and VCR-backed tests with checked-in cassettes underpy/src/braintrust/integrations/llama_stack/cassettes/<version>/— not mock-based coverage.Blocked on spec changes (braintrustdata/braintrust-spec)
These surfaces exist in
llama-stack-client0.7.5 but cannot be instrumented conformantly today, because the spec does not define a shape for them. They should be moved out of the "instrument this" list and tracked as spec work.Reranking —
client.alpha.inference.rerank(). This is the one method the realalpha.inferenceresource exposes, andinstrumentation-guide.md§TODO names it as explicitly unspecified: "Reranking APIs — instrumentation for reranking endpoints (e.g. Coherererank, Jina Reranker), including input/output structure and relevance score metrics." Needs a spec addition first.Safety / shields / moderation —
client.safety.run_shield(),client.moderations.create(),client.shields.*. No sanctioned span type or payload shape exists.features/classifiers.mdis not applicable: it specifies eval-framework classifier functions (EvalClassifier,Classification, theclassifiersfield on the evaluator interface), not provider-side guardrail/moderation API calls. Anyshieldsmetadata would also violate the §Metadata allowlist.Retrieval / vector stores —
client.vector_io.query(),client.vector_stores.search(). The guide specifiestoolspans only for model-initiated calls inside an agentic loop (§Child tool spans). A directly user-issued retrieval call has no defined span shape.metadata.providerfor a self-hosted routing gateway. Llama Stack routes to Ollama, vLLM, TGI, Together, Fireworks, SambaNova, and Meta endpoints, and the OpenAI-compatible response does not reliably expose which backend served the request.features/token-and-cost-metrics.mdframesprovideras the pricing surface, but gives no guidance for local/self-hosted inference where no pricing exists at all. A short clarification would let this integration pick a value defensibly instead of guessing.Strict metadata allowlist vs. shipped practice. Flagging for consistency, not to license a violation here: existing integrations already emit keys outside the §Metadata allowlist (e.g.
agent_id,guardrails,safe_promptand the responseid/object/createdin the Mistral integration;status,service_tier,backgroundin the OpenAI Responses path), andintegrations/utils.pyemits adurationmetric that is not in the §Metrics Reference table. Either the spec's allowlist needs to widen or this issue should not treat prior art as precedent for extra metadata.Suggested edits to this issue
client.inference.*table; replace with the real surfaces:client.chat.completions.create,client.completions.create,client.embeddings.create,client.responses.create, plus theAsyncLlamaStackClientequivalents.openai.OpenAIsubclass, sowrap_openai()does not apply", but replace "Llama Stack-specific API shape" with "OpenAI-compatible payloads on/v1/chat/completions,/v1/embeddings, and/v1/responses", and add that conversion to OpenAI Chat Completions format is nonetheless required (no UI normalizer exists for Llama Stack) and is effectively a passthrough.chat.completions/completions/embeddings→ onellmspan;responses.createwithtools→taskparent + childllm/toolspans;responses.createwithout tools → onellmspan. Resolve thetask-vs-llmparent question against the existing OpenAI Responses behavior noted above.agent_id,session_id,turn_id,conversation,previous_response_id,guardrails,max_infer_iters,safety_identifier,prompt_cache_key,service_tier,seed,user,n,logit_bias,logprobs,top_logprobs,reasoning_effort,include,background.prompt_tokens→prompt_tokens,completion_tokens→completion_tokens,total_tokens→**tokens**,completion_tokens_details.reasoning_tokens→completion_reasoning_tokens,prompt_tokens_details.cached_tokens→prompt_cached_tokens; omit anything the provider did not report, never fabricate zeros.metadata.providerunconditional ("llama-stack") and sourcemetadata.modelfrom the response, not the request.time_to_first_tokento the streaming requirement.features/embeddings.md: onellmspan, canonical{inputs, output_dimensions}input (map thedimensionsparam),{count}-only output, no vectors/dimensions/norms, nocompletion_tokens.metadata.tools/tool_choice/parallel_tool_calls/max_tool_calls; JSON-string tool-call arguments; top-levelerroron failure; attachment conversion forinput_image/input_fileparts;context.span_originvia_INSTRUMENTATION = "llama-stack-auto"; span naming; child-span ordering; parent-span token aggregation.[tool.braintrust.cassette-dirs]entry,test_llama_stacknox session, CI workflow inclusion, and VCR cassettes (not mocks).braintrust-spec" section rather than listing them as instrumentable work.No files were modified, no labels changed, and no PR was opened as part of this review.
AbhiPrasad commentedon Oct 14, 2026
Closing as not planned. The
inference/agentsAPIs described here no longer exist in current llama-stack-client releases, payloads are now OpenAI-compatible, and adoption is low (~9.5k weekly downloads).