PageSourceSearch

https://waxell.ai/docs/assets/js/e7f1fee4.554df5ea.js

js waxell.ai collected 2026-09-28 06:45:47 UTC 28,158 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkwebsite=globalThis.webpackChunkwebsite||[]).push([[1820],{34193(e,n,s){s.r(n),s.d(n,{assets:()=>a,contentTitle:()=>t,default:()=>h,frontMatter:()=>l,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"observe/troubleshooting/faq","title":"FAQ","description":"Frequently asked questions about the Waxell Observe SDK -- setup, instrumentation, governance, and more.","source":"@site/docs/observe/troubleshooting/faq.md","sourceDirName":"observe/troubleshooting","slug":"/observe/troubleshooting/faq","permalink":"/docs/observe/troubleshooting/faq","draft":false,"unlisted":false,"editUrl":"https://gitlab.com/waxell/agentforge/-/edit/main/website/docs/observe/troubleshooting/faq.md","tags":[],"version":"current","sidebarPosition":1,"frontMatter":{"sidebar_position":1,"title":"FAQ","description":"Frequently asked questions about the Waxell Observe SDK -- setup, instrumentation, governance, and more.","keywords":["waxell","observe","faq","questions","help","troubleshooting"]},"sidebar":"observeSidebar","previous":{"title":"Prompt Guard","permalink":"/docs/observe/features/prompt-guard"},"next":{"title":"Common Errors","permalink":"/docs/observe/troubleshooting/common-errors"}}');var i=s(74848),o=s(28453);const l={sidebar_position:1,title:"FAQ",description:"Frequently asked questions about the Waxell Observe SDK -- setup, instrumentation, governance, and more.",keywords:["waxell","observe","faq","questions","help","troubleshooting"]},t="Frequently Asked Questions",a={},c=[{value:"Setup &amp; Configuration",id:"setup--configuration",level:2},{value:"Do I need to call <code>init()</code> before importing LLM SDKs?",id:"do-i-need-to-call-init-before-importing-llm-sdks",level:3},{value:"Can I call <code>init()</code> multiple times?",id:"can-i-call-init-multiple-times",level:3},{value:"How do I disable the SDK in tests or CI?",id:"how-do-i-disable-the-sdk-in-tests-or-ci",level:3},{value:"What&#39;s the difference between <code>api_key</code> and <code>api_url</code>?",id:"whats-the-difference-between-api_key-and-api_url",level:3},{value:"What happens if the SDK can&#39;t reach the control plane?",id:"what-happens-if-the-sdk-cant-reach-the-control-plane",level:3},{value:"Instrumentation",id:"instrumentation",level:2},{value:"Should I use decorators or the context manager?",id:"should-i-use-decorators-or-the-context-manager",level:3},{value:"Why are some decorators named <code>reasoning_dec</code>, <code>retry_dec</code>, <code>step_dec</code> instead of <code>reasoning</code>, <code>retry</code>, <code>step</code>?",id:"why-are-some-decorators-named-reasoning_dec-retry_dec-step_dec-instead-of-reasoning-retry-step",level:3},{value:"Do decorators work on sync functions?",id:"do-decorators-work-on-sync-functions",level:3},{value:"What happens if I use behavior decorators outside an <code>@observe</code> scope?",id:"what-happens-if-i-use-behavior-decorators-outside-an-observe-scope",level:3},{value:"Does auto-instrumentation capture everything?",id:"does-auto-instrumentation-capture-everything",level:3},{value:"How do I see what libraries were auto-instrumented?",id:"how-do-i-see-what-libraries-were-auto-instrumented",level:3},{value:"Can I exclude specific libraries from auto-instrumentation?",id:"can-i-exclude-specific-libraries-from-auto-instrumentation",level:3},{value:"Governance &amp; Policies",id:"governance--policies",level:2},{value:"What&#39;s the difference between <code>enforce_policy</code> and <code>mid_execution_governance</code>?",id:"whats-the-difference-between-enforce_policy-and-mid_execution_governance",level:3},{value:"What is <code>on_policy_block</code>?",id:"what-is-on_policy_block",level:3},{value:"Can I check policies manually without using <code>@observe</code>?",id:"can-i-check-policies-manually-without-using-observe",level:3},{value:"Data &amp; Privacy",id:"data--privacy",level:2},{value:"Does the SDK send my prompts and responses to Waxell?",id:"does-the-sdk-send-my-prompts-and-responses-to-waxell",level:3},{value:"What is Prompt Guard?",id:"what-is-prompt-guard",level:3},{value:"Can I use Waxell with on-premises LLM providers?",id:"can-i-use-waxell-with-on-premises-llm-providers",level:3},{value:"Sessions &amp; Users",id:"sessions--users",level:2},{value:"How do sessions work?",id:"how-do-sessions-work",level:3},{value:"How does user tracking work?",id:"how-does-user-tracking-work",level:3},{value:"Performance",id:"performance",level:2},{value:"What&#39;s the overhead of the SDK?",id:"whats-the-overhead-of-the-sdk",level:3},{value:"Does the SDK add latency to my LLM calls?",id:"does-the-sdk-add-latency-to-my-llm-calls",level:3},{value:"Can I flush telemetry manually for long-running agents?",id:"can-i-flush-telemetry-manually-for-long-running-agents",level:3},{value:"Framework Integration",id:"framework-integration",level:2},{value:"Does Waxell work with LangChain?",id:"does-waxell-work-with-langchain",level:3},{value:"Does Waxell work with CrewAI / LlamaIndex / AutoGen?",id:"does-waxell-work-with-crewai--llamaindex--autogen",level:3},{value:"Can I use Waxell with multiple LLM providers in the same agent?",id:"can-i-use-waxell-with-multiple-llm-providers-in-the-same-agent",level:3}];function d(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",hr:"hr",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"frequently-asked-questions",children:"Frequently Asked Questions"})}),"\n",(0,i.jsx)(n.h2,{id:"setup--configuration",children:"Setup & Configuration"}),"\n",(0,i.jsxs)(n.h3,{id:"do-i-need-to-call-init-before-importing-llm-sdks",children:["Do I 
1need to call ",(0,i.jsx)(n.code,{children:"init()"})," before importing LLM SDKs?"]}),"\n",(0,i.jsxs)(n.p,{children:["Yes. ",(0,i.jsx)(n.code,{children:"waxell.init()"})," monkey-patches LLM libraries to add auto-instrumentation. If you import ",(0,i.jsx)(n.code,{children:"openai"})," or ",(0,i.jsx)(n.code,{children:"anthropic"})," before calling ",(0,i.jsx)(n.code,{children:"init()"}),", those imports get the un-patched versions and LLM calls won't be captured automatically."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:"# Correct\nimport waxell_observe as waxell\nwaxell.init()\nfrom openai import OpenAI  # patched\n\n# Wrong -- OpenAI is imported before init()\nfrom openai import OpenAI  # un-patched\nimport waxell_observe as waxell\nwaxell.init()  # too late for OpenAI\n"})}),"\n",(0,i.jsxs)(n.p,{children:["If you can't control import order (e.g., framework code imports LLM SDKs at module level), use the ",(0,i.jsx)(n.a,{href:"../api/python-sdk#drop-in-imports",children:"drop-in imports"})," instead:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:"from waxell_observe.openai import openai     # always patched\nfrom waxell_observe.anthropic import anthropic  # always patched\n"})}),"\n",(0,i.jsxs)(n.h3,{id:"can-i-call-init-multiple-times",children:["Can I call ",(0,i.jsx)(n.code,{children:"init()"})," multiple times?"]}),"\n",(0,i.jsxs)(n.p,{children:["Yes. ",(0,i.jsx)(n.code,{children:"init()"})," is idempotent -- only the first call takes effect. Subsequent calls are no-ops."]}),"\n",(0,i.jsx)(n.h3,{id:"how-do-i-disable-the-sdk-in-tests-or-ci",children:"How do I disable the SDK in tests or CI?"}),"\n",(0,i.jsxs)(n.p,{children:["Set the ",(0,i.jsx)(n.code,{children:"WAXELL_OBSERVE"})," environment variable to ",(0,i.jsx)(n.code,{children:"false"}),", ",(0,i.jsx)(n.code,{children:"0"}),", or ",(0,i.jsx)(n.code,{children:"no"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"WAXELL_OBSERVE=false pytest\n"})}),"\n",(0,i.jsxs)(n.p,{children:["This makes ",(0,i.jsx)(n.code,{children:"init()"})," skip all initialization. Decorators and convenience functions become no-ops. Your code runs normally with zero overhead."]}),"\n",(0,i.jsxs)(n.h3,{id:"whats-the-difference-between-api_key-and-api_url",children:["What's the difference between ",(0,i.jsx)(n.code,{children:"api_key"})," and ",(0,i.jsx)(n.code,{children:"api_url"}),"?"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:(0,i.jsx)(n.code,{children:"api_url"})}),": Your Waxell control plane URL (e.g., ",(0,i.jsx)(n.code,{children:"https://acme.waxell.dev"}),"). This is where your dashboard lives."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:(0,i.jsx)(n.code,{children:"api_key"})}),": A secret key starting with ",(0,i.jsx)(n.code,{children:"wax_sk_"})," that authenticates requests to your control plane."]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Both can be set via constructor arguments, environment variables (",(0,i.jsx)(n.code,{children:"WAXELL_API_URL"}),", ",(0,i.jsx)(n.code,{children:"WAXELL_API_KEY"}),"), or the CLI config file (",(0,i.jsx)(n.code,{children:"~/.waxell/config"}),")."]}),"\n",(0,i.jsx)(n.h3,{id:"what-happens-if-the-sdk-cant-reach-the-control-plane",children:"What happens if the SDK can't reach the control plane?"}),"\n",(0,i.jsx)(n.p,{children:"The SDK is designed to never break your agent. If the control plane is unreachable:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"init()"})," succeeds (tracing setup failure is logged as a warning)"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"@observe"})," runs your function normally and logs a warning about the failed run"]}),"\n",(0,i.jsx)(n.li,{children:"Auto-instrumentation continues to work locally (spans are captured but not exported)"}),"\n",(0,i.jsx)(n.li,{children:"No exceptions are raised from telemetry failures"}),"\n"]}),"\n",(0,i.jsx)(n.hr,{}),"\n",(0,i.jsx)(n.h2,{id:"instrumentation",children:"Instrumentation"}),"\n",(0,i.jsx)(n.h3,{id:"should-i-use-decorators-or-the-context-manager",children:"Should I use decorators or the context manager?"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Start with decorators."})," They cover 90% of use cases with less code:"]}),"\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{children:"Situation"}),(0,i.jsx)(n.th,{children:"Use"})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"Single function = one agent run"}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"@observe"})})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"Tool, retrieval, decision, step functions"}),(0,i.jsxs)(n.td,{children:[(0,i.jsx)(n.code,{children:"@tool"}),", ",(0,i.jsx)(n.code,{children:"@retrieval"}),", ",(0,i.jsx)(n.code,{children:"@decision"}),", ",(0,i.jsx)(n.code,{children:"@step_dec"})]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"Inline enrichment (scores, tags)"}),(0,i.jsxs)(n.td,{children:[(0,i.jsx)(n.code,{children:"waxell.score()"}),", ",(0,i.jsx)(n.code,{children:"waxell.tag()"})]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"Multi-step orchestration spanning many functions"}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"WaxellContext"})})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"Batch processing with many runs per loop"}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"WaxellContext"})})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:"Need mid-execution policy checks between steps"}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"WaxellContext"})})]})]})]}),"\n",(0,i.jsxs)(n.p,{children:["See the ",(0,i.jsx)(n.a,{href:"../integrations/context-manager",children:"Context Manager"})," page for advanced scenarios."]}),"\n",(0,i.jsxs)(n.h3,{id:"why-are-some-decorators-named-reasoning_dec-retry_dec-step_dec-instead-of-reasoning-retry-step",children:["Why are some decorators named ",(0,i.jsx)(n.code,{children:"reasoning_dec"}),", ",(0,i.jsx)(n.code,{children:"retry_dec"}),", ",(0,i.jsx)(n.code,{children:"step_dec"})," instead of ",(0,i.jsx)(n.code,{children:"reasoning"}),", ",(0,i.jsx)(n.code,{children:"retry"}),", ",(0,i.jsx)(n.code,{children:"step"}),"?"]}),"\n",(0,i.jsxs)(n.p,{children:["The names ",(0,i.jsx)(n.code,{children:"reasoning"}),", ",(0,i.jsx)(n.code,{children:"retry"}),", and ",(0,i.jsx)(n.code,{children:"step"})," collide with the module-level convenience functions (",(0,i.jsx)(n.code,{children:"waxell.reason()"}),", ",(0,i.jsx)(n.code,{children:"waxell.retry()"}),", ",(0,i.jsx)(n.code,{children:"waxell.step()"}),"). To avoid shadowing, the decorators are exported with a ",(0,i.jsx)(n.code,{children:"_dec"})," suffix:"]}),"\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{children:"Decorator"}),(0,i.jsx)(n.th,{children:"Convenience function"})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"@w
1axell.reasoning_dec()"})}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"waxell.reason()"})})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"@waxell.retry_dec()"})}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"waxell.retry()"})})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"@waxell.step_dec()"})}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"waxell.step()"})})]})]})]}),"\n",(0,i.jsxs)(n.p,{children:["The other decorators (",(0,i.jsx)(n.code,{children:"@tool"}),", ",(0,i.jsx)(n.code,{children:"@decision"}),", ",(0,i.jsx)(n.code,{children:"@retrieval"}),") don't collide because their convenience function equivalents use different names (",(0,i.jsx)(n.code,{children:"waxell.retrieve()"}),", ",(0,i.jsx)(n.code,{children:"waxell.decide()"}),")."]}),"\n",(0,i.jsx)(n.h3,{id:"do-decorators-work-on-sync-functions",children:"Do decorators work on sync functions?"}),"\n",(0,i.jsxs)(n.p,{children:["Yes. All decorators (",(0,i.jsx)(n.code,{children:"@observe"}),", ",(0,i.jsx)(n.code,{children:"@tool"}),", ",(0,i.jsx)(n.code,{children:"@decision"}),", ",(0,i.jsx)(n.code,{children:"@retrieval"}),", ",(0,i.jsx)(n.code,{children:"@reasoning_dec"}),", ",(0,i.jsx)(n.code,{children:"@retry_dec"}),", ",(0,i.jsx)(n.code,{children:"@step_dec"}),") work with both sync and async functions. The SDK detects the function type and wraps accordingly."]}),"\n",(0,i.jsxs)(n.h3,{id:"what-happens-if-i-use-behavior-decorators-outside-an-observe-scope",children:["What happens if I use behavior decorators outside an ",(0,i.jsx)(n.code,{children:"@observe"})," scope?"]}),"\n",(0,i.jsx)(n.p,{children:"They're no-ops. Your function executes normally with zero overhead -- no context lookup, no recording, no errors. This means you can decorate functions once and call them from both instrumented and non-instrumented code paths."}),"\n",(0,i.jsx)(n.h3,{id:"does-auto-instrumentation-capture-everything",children:"Does auto-instrumentation capture everything?"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"waxell.init()"})," auto-instruments 200+ libraries for LLM calls, vector DB queries, HTTP requests, database calls, and more. It captures:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Model name, token counts, cost, latency"}),"\n",(0,i.jsxs)(n.li,{children:["Prompt/response previews (if ",(0,i.jsx)(n.code,{children:"capture_content=True"}),")"]}),"\n",(0,i.jsx)(n.li,{children:"Streaming responses (tokens are aggregated)"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["It does ",(0,i.jsx)(n.strong,{children:"not"})," capture:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Business logic decisions (use ",(0,i.jsx)(n.code,{children:"@decision"})," or ",(0,i.jsx)(n.code,{children:"waxell.decide()"}),")"]}),"\n",(0,i.jsxs)(n.li,{children:["Custom tool calls (use ",(0,i.jsx)(n.code,{children:"@tool"}),")"]}),"\n",(0,i.jsxs)(n.li,{children:["Quality scores (use ",(0,i.jsx)(n.code,{children:"waxell.score()"}),")"]}),"\n",(0,i.jsxs)(n.li,{children:["Tags and metadata (use ",(0,i.jsx)(n.code,{children:"waxell.tag()"}),", ",(0,i.jsx)(n.code,{children:"waxell.metadata()"}),")"]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"how-do-i-see-what-libraries-were-auto-instrumented",children:"How do I see what libraries were auto-instrumented?"}),"\n",(0,i.jsxs)(n.p,{children:["Use ",(0,i.jsx)(n.code,{children:"waxell.diagnose()"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'import waxell_observe as waxell\nwaxell.init()\n\ninfo = waxell.diagnose()\nprint(info["active_instrumentors"])   # ["openai", "anthropic", ...]\nprint(info["detected_libraries"])     # {"openai": "1.52.0", ...}\nprint(info["sdk_version"])            # "0.0.40"\n'})}),"\n",(0,i.jsx)(n.h3,{id:"can-i-exclude-specific-libraries-from-auto-instrumentation",children:"Can I exclude specific libraries from auto-instrumentation?"}),"\n",(0,i.jsxs)(n.p,{children:["Yes, use the ",(0,i.jsx)(n.code,{children:"exclude"})," parameter on ",(0,i.jsx)(n.code,{children:"init()"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'waxell.init(exclude=["litellm", "mcp"])\n'})}),"\n",(0,i.jsx)(n.p,{children:"Or via environment variable:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"WAXELL_EXCLUDE=litellm,mcp python my_agent.py\n"})}
1),"\n",(0,i.jsxs)(n.p,{children:["For infrastructure libraries (HTTP, DB, caches), use ",(0,i.jsx)(n.code,{children:"infra_exclude"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'waxell.init(infra_exclude=["celery", "grpc"])\n'})}),"\n",(0,i.jsx)(n.hr,{}),"\n",(0,i.jsx)(n.h2,{id:"governance--policies",children:"Governance & Policies"}),"\n",(0,i.jsxs)(n.h3,{id:"whats-the-difference-between-enforce_policy-and-mid_execution_governance",children:["What's the difference between ",(0,i.jsx)(n.code,{children:"enforce_policy"})," and ",(0,i.jsx)(n.code,{children:"mid_execution_governance"}),"?"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:(0,i.jsx)(n.code,{children:"enforce_policy=True"})})," (default): Checks policies once when the run starts. If blocked, raises ",(0,i.jsx)(n.code,{children:"PolicyViolationError"})," before your function executes."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:(0,i.jsx)(n.code,{children:"mid_execution_governance=True"})}),": In addition to the entry check, every ",(0,i.jsx)(n.code,{children:"record_step()"})," call flushes data to the control plane and re-checks policies. If a policy blocks mid-execution, raises ",(0,i.jsx)(n.code,{children:"PolicyViolationError"})," from inside your function."]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Use ",(0,i.jsx)(n.code,{children:"mid_execution_governance"})," for long-running agents where you want to catch budget overruns or policy violations before the agent finishes."]}),"\n",(0,i.jsxs)(n.h3,{id:"what-is-on_policy_block",children:["What is ",(0,i.jsx)(n.code,{children:"on_policy_block"}),"?"]}),"\n",(0,i.jsxs)(n.p,{children:["A callback that fires when a policy blocks execution. Instead of immediately raising ",(0,i.jsx)(n.code,{children:"PolicyViolationError"}),", the SDK calls your handler and lets you decide what to do:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'@waxell.observe(\n    agent_name="my-agent",\n    on_policy_block=waxell.prompt_approval,  # Terminal Y/N prompt\n)\nasync def my_agent(query: str):\n    ...\n'})}),"\n",(0,i.jsx)(n.p,{children:"Built-in handlers:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"waxell.prompt_approval"})," -- Interactive terminal prompt"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"waxell.auto_approve"})," -- Always proceed (for testing)"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"waxell.auto_deny"})," -- Always block (for testing)"]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"You can write custom handlers for Slack approvals, webhook-based workflows, etc."}),"\n",(0,i.jsxs)(n.h3,{id:"can-i-check-policies-manually-without-using-observe",children:["Can I check policies manually without using ",(0,i.jsx)(n.code,{children:"@observe"}),"?"]}),"\n",(0,i.jsx)(n.p,{children:"Yes, through the client:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'from waxell_observe import WaxellObserveClient\n\nclient = WaxellObserveClient()\nresult = await client.check_policy(agent_name="my-agent")\nif result.blocked:\n    print(f"Blocked: {result.reason}")\n'})}),"\n",(0,i.jsxs)(n.p,{children:["Or inside a ",(0,i.jsx)(n.code,{children:"WaxellContext"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'async with WaxellContext(agent_name="my-agent") as ctx:\n    # ... do some work ...\n    policy = await ctx.check_policy()\n    if policy.blocked:\n        return "Agent was blocked by policy"\n'})}),"\n",(0,i.jsx)(n.hr,{}),"\n",(0,i.jsx)(n.h2,{id:"data--privacy",children:"Data & Privacy"}),"\n",(0,i.jsx)(n.h3,{id:"does-the-sdk-send-my-prompts-and-responses-to-waxell",children:"Does the SDK send my prompts and responses to Waxell?"}),"\n",(0,i.jsxs)(n.p,{children:["By default, ",(0,i.jsx)(n.strong,{children:"no"}),". Prompt and response content is not included in telemetry. Set ",(0,i.jsx)(n.code,{children:"capture_content=True"})," on ",(0,i.jsx)(n.code,{children:"init()"})," to opt in:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:"waxell.init(capture_content=True)\n"})}),"\n",(0,i.jsx)(n.p,{children:"Without this flag, only metadata is captured: model name, token counts, cost, latency, and timing."}),"\n",(0,i.jsx)(n.h3,{id:"what-is-prompt-guard",children:"What is Prompt Guard?"}),"\n",(0,i.jsx)(n.p,{children:"Prompt Guard scans LLM inputs for PII (emails, phone numbers, SSNs), leaked credentials (API keys, tokens), and injection attempts before they reach the LLM provider. Enable it with:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'waxell.init(\n    prompt_guard=True,              # Client-side regex detection\n    prompt_guard_server=True,       # Server-side ML detection (more accurate)\n    prompt_guard_action="redact",   # "block", "warn", or "redact"\n)\n'})}),"\n",(0,i.jsx)(n.h3,{id:"can-i-use-waxell-with-on-premises-llm-providers",children:"Can I use Waxell with on-premises LLM providers?"}),"\n",(0,i.jsxs)(n.p,{children:["Yes. The SDK instruments any OpenAI-compatible API, local models (Ollama, vLLM, SGLang), and HuggingFace models. Cost estimation returns ",(0,i.jsx)(n.code,{children:"0.0"})," for local/free providers."]}),"\n",(0,i.jsx)(n.hr,{}),"\n",(0,i.jsx)(n.h2,{id:"sessions--users",children:"Sessions & Users"}),"\n",(0,i.jsx)(n.h3,{id:"how-do-sessions-work",children:"How do sessions work?"}),"\n",(0,i.jsxs)(n.p,{children:["A session groups related runs together. Pass the same ",(0,i.jsx)(n.code,{children:"session_id"})," to multiple runs:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'@waxell.observe(agent_name="chatbot", session_id="sess_abc123")\nasync def handle_message(msg: str):\n    ...\n\n# Or dynamically at call time:\nawait handle_message("Hello", session_id="sess_abc123")\nawait handle_message("Follow up", session_id="sess_abc123")\n'})}),"\n",(0,i.jsxs)(n.p,{children:["Use ",(0,i.jsx)(n.code,{children:"generate_session_id()"})," to create unique session IDs."]}),"\n",(0,i.jsx)(n.h3,{id:"how-does-user-tracking-work",children:"How does user tracking work?"}),"\n",(0,i.jsxs)(n.p,{children:["Pass ",(0,i.jsx)(n.code,{children:"user_id"})," to attribute runs, costs, and usage to specific end users:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'@waxell.observe(agent_name="chatbot", user_id="user_456")\nasync def handle_message(msg: str):\n    ...\n'})}),"\n",(0,i.jsx)(n.p,{children:"This enables per-user analytics, c
1ost attribution, and user-scoped policies in the dashboard."}),"\n",(0,i.jsx)(n.hr,{}),"\n",(0,i.jsx)(n.h2,{id:"performance",children:"Performance"}),"\n",(0,i.jsx)(n.h3,{id:"whats-the-overhead-of-the-sdk",children:"What's the overhead of the SDK?"}),"\n",(0,i.jsx)(n.p,{children:"Minimal. The SDK:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Buffers all telemetry in memory and flushes asynchronously on context exit"}),"\n",(0,i.jsx)(n.li,{children:"Uses non-blocking HTTP calls for the control plane API"}),"\n",(0,i.jsx)(n.li,{children:"Auto-instrumentation adds microsecond-level overhead per LLM call"}),"\n",(0,i.jsxs)(n.li,{children:["Convenience functions (",(0,i.jsx)(n.code,{children:"waxell.score()"}),", ",(0,i.jsx)(n.code,{children:"waxell.tag()"}),", etc.) are simple dict appends"]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"If the control plane is unreachable, telemetry is dropped silently -- no retry loops or blocking waits."}),"\n",(0,i.jsx)(n.h3,{id:"does-the-sdk-add-latency-to-my-llm-calls",children:"Does the SDK add latency to my LLM calls?"}),"\n",(0,i.jsx)(n.p,{children:"No. Auto-instrumentation wraps LLM SDK methods with timing and metadata capture, but does not add any network calls in the LLM request path. All telemetry is flushed asynchronously after the LLM call completes."}),"\n",(0,i.jsx)(n.h3,{id:"can-i-flush-telemetry-manually-for-long-running-agents",children:"Can I flush telemetry manually for long-running agents?"}),"\n",(0,i.jsxs)(n.p,{children:["Yes. Use ",(0,i.jsx)(n.code,{children:"waxell.flush()"})," (async) or ",(0,i.jsx)(n.code,{children:"waxell.flush_sync()"})," to flush buffered data without waiting for the context to exit:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'@waxell.observe(agent_name="long-runner")\nasync def long_running_agent():\n    for batch in data:\n        process(batch)\n        await waxell.flush()  # Send data to control plane now\n'})}),"\n",(0,i.jsx)(n.hr,{}),"\n",(0,i.jsx)(n.h2,{id:"framework-integration",children:"Framework Integration"}),"\n",(0,i.jsx)(n.h3,{id:"does-waxell-work-with-langchain",children:"Does Waxell work with LangChain?"}),"\n",(0,i.jsx)(n.p,{children:"Yes. Use the callback handler:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'from waxell_observe.integrations.langchain import WaxellLangChainHandler\n\nhandler = WaxellLangChainHandler(agent_name="my-chain")\nresult = chain.invoke(input, config={"callbacks": [handler]})\nhandler.flush_sync(result={"output": result})\n'})}),"\n",(0,i.jsx)(n.h3,{id:"does-waxell-work-with-crewai--llamaindex--autogen",children:"Does Waxell work with CrewAI / LlamaIndex / AutoGen?"}),"\n",(0,i.jsxs)(n.p,{children:["Yes. These frameworks are auto-instrumented by ",(0,i.jsx)(n.code,{children:"waxell.init()"}),". LLM calls, tool invocations, and agent delegations are captured automatically."]}),"\n",(0,i.jsx)(n.h3,{id:"can-i-use-waxell-with-multiple-llm-providers-in-the-same-agent",children:"Can I use Waxell with multiple LLM providers in the same agent?"}),"\n",(0,i.jsxs)(n.p,{children:["Yes. Auto-instrumentation patches all installed provider SDKs. A single ",(0,i.jsx)(n.code,{children:"@observe"})," run can contain calls to OpenAI, Anthropic, Groq, and others -- all captured in the same trace."]})]})}function h(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},28453(e,n,s){s.d(n,{R:()=>l,x:()=>t});var r=s(96540);const i={},o=r.createContext(i);function l(e){const n=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function t(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:l(e.components),r.createElement(o.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.