PageSourceSearch

https://waxell.ai/docs/assets/js/b067ad40.f3b2a0c9.js

js waxell.ai collected 2026-09-28 06:45:26 UTC 17,224 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkwebsite=globalThis.webpackChunkwebsite||[]).push([[871],{86210(e,n,r){r.r(n),r.d(n,{assets:()=>o,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>t,toc:()=>c});const t=JSON.parse('{"id":"observe/overview","title":"Waxell Observe","description":"Lightweight observability and governance for any Python AI agent framework. Track LLM calls, manage costs, and enforce policies without rewriting your agents.","source":"@site/docs/observe/overview.md","sourceDirName":"observe","slug":"/observe/overview","permalink":"/docs/observe/overview","draft":false,"unlisted":false,"editUrl":"https://gitlab.com/waxell/agentforge/-/edit/main/website/docs/observe/overview.md","tags":[],"version":"current","sidebarPosition":1,"frontMatter":{"sidebar_position":1,"title":"Waxell Observe","description":"Lightweight observability and governance for any Python AI agent framework. Track LLM calls, manage costs, and enforce policies without rewriting your agents.","keywords":["waxell","observe","observability","ai agents","llm tracking","governance","cost management"]},"sidebar":"observeSidebar","next":{"title":"Quickstart: Observe Your Agents","permalink":"/docs/observe/quickstart"}}');var s=r(74848),i=r(28453);const a={sidebar_position:1,title:"Waxell Observe",description:"Lightweight observability and governance for any Python AI agent framework. Track LLM calls, manage costs, and enforce policies without rewriting your agents.",keywords:["waxell","observe","observability","ai agents","llm tracking","governance","cost management"]},l="Waxell Observe",o={},c=[{value:"Fastest Path: Auto-Instrumentation",id:"fastest-path-auto-instrumentation",level:2},{value:"The Decorator Pattern (Recommended)",id:"the-decorator-pattern-recommended",level:2},{value:"Decorator Reference",id:"decorator-reference",level:3},{value:"Convenience Functions",id:"convenience-functions",level:3},{value:"Advanced: Context Manager",id:"advanced-context-manager",level:2},{value:"LangChain Integration",id:"langchain-integration",level:2},{value:"What You Get",id:"what-you-get",level:2},{value:"Framework Compatibility",id:"framework-compatibility",level:2},{value:"Next Steps",id:"next-steps",level:2}];function d(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,i.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.header,{children:(0,s.jsx)(n.h1,{id:"waxell-observe",children:"Waxell Observe"})}),"\n",(0,s.jsx)(n.p,{children:"You already have agents -- add observability in 2 lines of code."}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Waxell Observe"})," is a lightweight Python package that brings LLM call tracking, cost management, and policy enforcement to any AI agent. It works with any Python agent framework -- LangChain, LlamaIndex, CrewAI, custom code, or anything else. No vendor lock-in, no runtime changes, no migration required."]}),"\n",(0,s.jsx)(n.h2,{id:"fastest-path-auto-instrumentation",children:"Fastest Path: Auto-Instrumentation"}),"\n",(0,s.jsx)(n.p,{children:"Two lines to automatically trace all LLM calls across 200+ providers:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:'import waxell_observe as waxell\nwaxell.init(api_key="wax_sk_...", api_url="https://acme.waxell.dev")\n\n# Import LLM SDKs AFTER init() -- they\'re now auto-instrumented\nfrom openai import OpenAI\n\nclient = OpenAI()\nresponse = client.chat.completions.create(\n    model="gpt-4o",\n    messages=[{"role": "user", "content": "Hello!"}]\n)\n# Automatically traced with model, tokens, cost, latency\n'})}),"\n",(0,s.jsx)(n.h2,{id:"the-decorator-pattern-recommended",children:"The Decorator Pattern (Recommended)"}),"\n",(0,s.jsxs)(n.p,{children:["Decorators are the primary way to instrument your agents. Wrap functions with ",(0,s.jsx)(n.code,{children:"@observe"})," and behavior decorators to get structured, rich traces with minimal code:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:'import waxell_observe as waxell\n\nwaxell.init(
1)\n\nfrom openai import AsyncOpenAI\n\nclient = AsyncOpenAI()\n\n\[email protected](source="pinecone")\nasync def search_docs(query: str) -> list[dict]:\n    return await vector_store.search(query, top_k=10)\n\n\[email protected](name="approach", options=["summarize", "compare", "deep_dive"])\nasync def choose_approach(query: str) -> dict:\n    return {"chosen": "deep_dive", "reasoning": "Query asks for detailed analysis"}\n\n\[email protected](tool_type="api")\nasync def run_analysis(docs: list) -> dict:\n    return await analysis_service.analyze(docs)\n\n\[email protected](agent_name="research-pipeline")\nasync def run_pipeline(query: str):\n    docs = await search_docs(query)\n    approach = await choose_approach(query)\n    analysis = await run_analysis(docs)\n\n    # Inline enrichment\n    waxell.score("quality", 0.92)\n    waxell.tag("domain", "research")\n\n    return {"result": analysis, "approach": approach["chosen"]}\n'})}),"\n",(0,s.jsxs)(n.p,{children:["Every decorated function inside ",(0,s.jsx)(n.code,{children:"@observe"})," is automatically recorded as a structured span. No manual ",(0,s.jsx)(n.code,{children:"ctx.record_*()"})," calls needed."]}),"\n",(0,s.jsx)(n.h3,{id:"decorator-reference",children:"Decorator Reference"}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Decorator"}),(0,s.jsx)(n.th,{children:"Purpose"}),(0,s.jsx)(n.th,{children:"What it captures"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"@waxell.observe()"})}),(0,s.jsx)(n.td,{children:"Agent run boundary"}),(0,s.jsx)(n.td,{children:"Inputs, outputs, policy checks, run lifecycle"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"@waxell.tool()"})}),(0,s.jsx)(n.td,{children:"Tool/function calls"}),(0,s.jsx)(n.td,{children:"Name, inputs, output, duration, status"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"@waxell.retrieval()"})}),(0,s.jsx)(n.td,{children:"RAG search operations"}),(0,s.jsx)(n.td,{children:"Query, documents, scores, source"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"@waxell.decision()"})}),(0,s.jsx)(n.td,{children:"Routing/classification"}),(0,s.jsx)(n.td,{children:"Chosen option, reasoning, confidence"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"@waxell.reasoning_dec()"})}),(0,s.jsx)(n.td,{children:"Chain-of-thought"}),(0,s.jsx)(n.td,{children:"Thought, evidence, conclusion"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"@waxell.step_dec()"})}),(0,s.jsx)(n.td,{children:"Pipeline steps"}),(0,s.jsx)(n.td,{children:"Step name and output"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"@waxell.retry_dec()"})}),(0,s.jsx)(n.td,{children:"Retry/fallback logic"}),(0,s.jsx)(n.td,{children:"Attempt count, strategy, errors"})]})]})]}),"\n",(0,s.jsx)(n.h3,{id:"convenience-functions",children:"Convenience Functions"}),"\n",(0,s.jsxs)(n.p,{children:["Use these anywhere inside an ",(0,s.jsx)(n.code,{children:"@observe"})," scope for inline enrichment:"]}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Function"}),(0,s.jsx)(n.th,{children:"Purpose"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"waxell.score(name, value)"})}),(0,s.jsx)(n.td,{children:"Quality scores (numeric, boolean, categorical)"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"waxell.tag(key, value)"})}),(0,s.jsx)(n.td,{children:"Searchable key-value tags"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"waxell.metadata(key, value)"})}),(0,s.jsx)(n.td,{children:"Arbitrary structured metadata"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"waxell.step(name, output=)"})}
1),(0,s.jsx)(n.td,{children:"Quick step recording"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"waxell.decide(name, chosen=)"})}),(0,s.jsx)(n.td,{children:"Inline decision recording"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"waxell.retrieve(query=, documents=)"})}),(0,s.jsx)(n.td,{children:"Inline retrieval recording"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"waxell.reason(step=, thought=)"})}),(0,s.jsx)(n.td,{children:"Inline reasoning recording"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"waxell.retry(attempt=, reason=)"})}),(0,s.jsx)(n.td,{children:"Inline retry recording"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"waxell.user_message(content)"})}),(0,s.jsx)(n.td,{children:"Record inbound user message"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"waxell.agent_response(content)"})}),(0,s.jsx)(n.td,{children:"Record outbound agent response"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"waxell.communication(channel=)"})}),(0,s.jsx)(n.td,{children:"Record outbound messages (Slack, email, etc.)"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsxs)(n.td,{children:[(0,s.jsx)(n.code,{children:"waxell.flush()"})," / ",(0,s.jsx)(n.code,{children:"waxell.flush_sync()"})]}),(0,s.jsx)(n.td,{children:"Flush buffered data for long-running agents"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"waxell.diagnose()"})}),(0,s.jsx)(n.td,{children:"Introspect SDK state and configuration"})]})]})]}),"\n",(0,s.jsx)(n.h2,{id:"advanced-context-manager",children:"Advanced: Context Manager"}),"\n",(0,s.jsxs)(n.p,{children:["For complex scenarios where decorators don't fit -- multi-step orchestration, batch processing, conditional context creation -- use ",(0,s.jsx)(n.code,{children:"WaxellContext"})," directly:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:'from waxell_observe import WaxellContext\n\nasync with WaxellContext(\n    agent_name="research-agent",\n    session_id="sess_abc123",\n    user_id="user_456",\n) as ctx:\n    result = await run_research_pipeline(query)\n    ctx.record_llm_call(model="claude-sonnet-4", tokens_in=500, tokens_out=200)\n    ctx.record_step("summarize", output={"summary": result})\n    ctx.set_result({"answer": result})\n'})}),"\n",(0,s.jsxs)(n.p,{children:["See the ",(0,s.jsx)(n.a,{href:"./integrations/context-manager",children:"Context Manager"})," page for the full API."]}),"\n",(0,s.jsx)(n.h2,{id:"langchain-integration",children:"LangChain Integration"}),"\n",(0,s.jsx)(n.p,{children:"Drop-in callback handler for any LangChain chain or agent:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:'from waxell_observe.integrations.langchain import WaxellLangChainHandler\n\nhandler = WaxellLangChainHandler(agent_name="langchain-agent")\nresult = chain.invoke(input, config={"callbacks": [handler]})\nhandler.flush_sync(result={"output": result})\n'})}),"\n",(0,s.jsx)(n.h2,{id:"what-you-get",children:"What You Get"}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Feature"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.strong,{children:"LLM Call Tracking"})}),(0,s.jsx)(n.td,{children:"Model, token counts, cost, prompt/response previews for every LLM call"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.strong,{children:"LLM Call Explorer"})}),(0,s.jsx)(n.td,{children:"Browse, filter, and inspect every LLM call with prompt/response viewer"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.strong,{children:"Session Tracking"})}),(0,s.jsx)(n.td,{children:"Group related runs by session for conversation-level analytics"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.strong,{children:"User Tracking"})}),(0,s.jsx)(n.td,{children:"Per-user cost attribution, usage patterns, and analytics"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.strong,{children:"Scoring"})}),(0,s.jsx)(n.td,{children:"Capture quality scores via SDK or UI annotations"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.strong,{children:"Annotation Queues"})}),(0,s.jsx)(n.td,{children:"Human review workflows for manual quality assessment"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.strong,{children:"Prompt Management"})}),(0,s.jsx)(n.td,{children:"Version-controlled prompts with labels, playground, and SDK retrieval"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.strong,{children:"Cost Analytics"})}),(0,s.jsx)(n.td,{children:"Model usage breakdown, per-user costs, custom pricing overrides"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.strong,{children:"Policy Enforcement"})}),(0,s.jsx)(n.td,{children:"Pre-execution and mid-execution checks with allow/block/warn/throttle actions"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.strong,{children:"Behavior Tracking"})}),(0,s.jsx)(n.td,{children:"Structured spans for tools, retrievals, decisions, reasoning, retries"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.strong,{children:"Approval Workflows"})}),(0,s.jsx)(n.td,{children:"Human-in-the-loop approval for policy-blocked actions"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.strong,{children:"Conversation Tracking"})}),(0,s.jsx)(n.td,{children:"Auto-captured conversation state, context utilization, message counts"})]})]})]}),"\n",(0,s.jsx)(n.h2,{id:"framework-compatibility",children:"Framework Compatibility"}),"\n",(0,s.jsx)(n.p,{children:"Waxell Observe works with any Python agent framework:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"OpenAI"})," -- auto-instrumentation or decorators"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Anthropic"})," -- auto-instrumentation or decorators"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"LangChain / LangGraph"})," -- first-class callback handler"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"LiteLLM"})," -- unified API for 100+ providers"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"LlamaIndex"})," -- auto-instrumentation or decorators"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"CrewAI"})," -- auto-instrumentation or decorators"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Custom frameworks"})," -- decorators or context manager"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Any Python code"})," -- if it runs Python, you can observe it"]}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"next-steps",children:"Next Steps"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"./quickstart",children:"Qu
1ickstart"})," -- Get up and running in 5 minutes"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"./integrations/decorator",children:"Decorator Pattern"})," -- Full ",(0,s.jsx)(n.code,{children:"@observe"})," reference with all parameters"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"./integrations/auto-instrumentation",children:"Auto-Instrumentation"})," -- Zero-code tracing for 200+ libraries"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"./features/behavior-tracking",children:"Behavior Tracking"})," -- Deep dive into tools, retrievals, decisions, reasoning"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"https://github.com/waxell-ai/claude-skills",children:"Claude Skills"})," -- Let your coding agent instrument and govern your agents for you"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"https://github.com/waxell-ai/waxell-agent-examples",children:"Examples on GitHub"})," -- Complete runnable agents for every provider and pattern"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"./troubleshooting/faq",children:"FAQ"})," -- Answers to common questions"]}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(d,{...e})}):d(e)}},28453(e,n,r){r.d(n,{R:()=>a,x:()=>l});var t=r(96540);const s={},i=t.createContext(s);function a(e){const n=t.useContext(i);return t.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:a(e.components),t.createElement(i.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.