@respan/cli 0.14.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1585 +0,0 @@
1
- // AUTO-GENERATED by scripts/embed-skill-refs.mjs
2
- // Source: skills/SKILL.md and skills/references/{tracing,gateway,prompts,evals,monitors}.md
3
- // Do not edit directly — edit the markdown sources and rebuild.
4
- export const SKILL_MD = `# Respan
5
-
6
- Use the Respan CLI and SDK for LLM observability — tracing, evals, prompts, datasets, and gateway routing.
7
-
8
- ## When To Use
9
-
10
- - **Set up tracing** (instrument your app to capture LLM calls — install + decorators, propagation, processors) → read [references/tracing.md](references/tracing.md)
11
- - **Set up gateway** (route LLM calls through the Respan proxy — wiring + model switching, fallbacks, caching) → read [references/gateway.md](references/gateway.md)
12
- - **Prompt management** (create, version, deploy) → read [references/prompts.md](references/prompts.md)
13
- - **Evals** (datasets, evaluators, experiments) → read [references/evals.md](references/evals.md)
14
- - **Monitors & automation** (alerts, online evals, webhooks) → read [references/monitors.md](references/monitors.md)
15
-
16
- Tracing and gateway are separate setups — configure one at a time, never both in the same pass.
17
-
18
- ## Core Principles
19
-
20
- 1. **Read the reference first.** Each reference file has the exact setup steps, API patterns, MCP tools, and CLI commands.
21
- 2. **Use MCP tools** for platform operations (prompts, datasets, evaluators, experiments, traces, logs).
22
- 3. **Use CLI** when MCP is not available: \`respan traces list\`, \`respan prompts list\`, etc.
23
- 4. **Fetch docs** for integration-specific details not covered in references.
24
-
25
- ## Quick Reference
26
-
27
- | Task | Reference / Command |
28
- |------|--------------------|
29
- | Set up tracing (steps + decorators, propagation) | [references/tracing.md](references/tracing.md) |
30
- | Set up gateway (steps + features) | [references/gateway.md](references/gateway.md) |
31
- | Prompt management | [references/prompts.md](references/prompts.md) |
32
- | Evals & experiments | [references/evals.md](references/evals.md) |
33
- | Monitors & automation | [references/monitors.md](references/monitors.md) |
34
- | List traces | \`respan traces list --limit 10\` |
35
- | View a trace | \`respan traces get <id>\` |
36
- | Check auth | \`respan auth status\` |
37
-
38
- ## Documentation Access
39
-
40
- Any doc page can be fetched as markdown:
41
- \`https://respan.ai/docs/integrations/openai-sdk.md\`
42
- \`https://respan.ai/docs/sdks/typescript-sdk/overview.md\`
43
-
44
- Full docs index: \`https://www.respan.ai/docs/llms.txt\`
45
-
46
- Platform: \`https://platform.respan.ai\`
47
- `;
48
- export const TRACING_MD = `# Tracing
49
-
50
- Set up and configure Respan SDK tracing: instrument an app to capture LLM calls as
51
- structured traces, then refine with decorators, context propagation, and processors.
52
-
53
- This file covers **both** the initial setup steps and the advanced reference. Tracing
54
- is a separate setup from the gateway — do one at a time, never both in the same pass.
55
-
56
- Docs: \`https://respan.ai/docs/documentation/features/tracing/traces/tracing-sdk.md\`
57
- Full docs index: \`https://www.respan.ai/docs/llms.txt\`
58
-
59
- ---
60
-
61
- ## Setup
62
-
63
- Use this section when the user asks to set up Respan tracing in their project.
64
-
65
- ### Hard Rules
66
-
67
- - **Interactive mode:** Ask the user questions when you need input. Do not assume.
68
- - **Only add Respan code.** Do not refactor or modify unrelated code.
69
- - **Pin exact versions.** Never use \`latest\` or unpinned ranges.
70
- - **Do not guess APIs.** Use only the patterns from the integration docs linked below.
71
- - **If Respan is already installed/configured, do not duplicate work.** Check for existing \`respan\` imports first.
72
- - **Read the code before proposing changes.** Understand the actual workflow, not just the dependencies.
73
-
74
- The API key is stored in \`.env\` as \`RESPAN_API_KEY\`.
75
-
76
- ### 1. Ask first: Auto or Full?
77
-
78
- **Before analyzing anything, ask the user which setup they want.** This is the first question — the two branches diverge immediately.
79
-
80
- 1. **Auto** — the fastest working path. For a supported direct SDK, install the core SDK and add \`Respan()\` (one line) to capture flat LLM spans. The Vercel AI SDK is an explicit-only exception: Auto must also add \`@respan/instrumentation-vercel\` and \`VercelAIInstrumentor\`, but still adds no decorators or manual workflow wrappers. Best for a quick start or a live demo: traces flowing in under two minutes.
81
- 2. **Full** — structured setup. Adds framework-specific instrumentation and/or workflow structure on top:
82
- - **Explicit instrumentor** — for agent frameworks (LangChain, CrewAI, OpenAI Agents, Claude Agent SDK, LlamaIndex, Haystack, …) so the framework's agent / tool / chain structure is captured.
83
- - **Decorators** — wrap the user's own functions with \`@workflow\` / \`@task\` for nested spans.
84
-
85
- Follow only the branch the user picks; skip the other one.
86
-
87
- > **Frameworks under Auto:** Auto normally does not install a framework instrumentor. If the app uses a framework (LangChain, CrewAI, an agents SDK), Auto captures only spans from a supported underlying direct SDK. **Direct Vercel AI SDK usage is different:** the Respan facade deliberately does not auto-discover it, and AI SDK calls are not covered by core-only setup. If the project directly calls the \`ai\` package and no higher-level framework owns its AI SDK telemetry, follow the Vercel branch below in both Auto and Full.
88
-
89
- ---
90
-
91
- ### Auto path
92
-
93
- **A1. Detect language, package manager, and integration** — inspect \`package.json\` and source imports for JS/TS, or \`pyproject.toml\` / \`requirements.txt\` for Python. Before choosing core-only initialization, check whether the JS/TS project directly imports and calls the \`ai\` package (Vercel AI SDK). Apply the priority rule first: if a higher-level integration owns AI SDK telemetry, use that integration instead of also adding Vercel instrumentation. For example, an Eve app must use \`EveInstrumentor\`; do not activate \`VercelAIInstrumentor\` or another \`@ai-sdk/otel\` bridge alongside it.
94
-
95
- **A2. Install the core SDK** (pin the exact current version):
96
- \`\`\`bash
97
- # Python
98
- pip install respan-ai
99
-
100
- # TypeScript
101
- npm install @respan/respan
102
- \`\`\`
103
-
104
- If the project uses Vercel AI SDK directly, fetch and follow its [integration doc](https://respan.ai/docs/integrations/vercel-ai-sdk.md), and pin exact compatible versions of:
105
-
106
- - \`@respan/respan\`
107
- - \`@respan/instrumentation-vercel\`
108
- - \`@ai-sdk/otel\` when the installed \`ai\` major version is 7 (AI SDK 4 through 6 do not require this adapter)
109
-
110
- **A3. Add initialization code** at the top of the entrypoint, before any LLM client is created:
111
- \`\`\`python
112
- # Python
113
- from respan import Respan
114
- Respan()
115
- \`\`\`
116
- \`\`\`typescript
117
- // TypeScript
118
- import { Respan } from "@respan/respan";
119
- const respan = new Respan();
120
- await respan.initialize();
121
- \`\`\`
122
-
123
- For a Vercel AI SDK project, use its required explicit instrumentor instead of the core-only TypeScript initialization above:
124
-
125
- \`\`\`typescript
126
- import { Respan } from "@respan/respan";
127
- import { VercelAIInstrumentor } from "@respan/instrumentation-vercel";
128
-
129
- const respan = new Respan({
130
- instrumentations: [new VercelAIInstrumentor()],
131
- });
132
- await respan.initialize();
133
- \`\`\`
134
-
135
- If the project already has a \`Respan\` instance, merge \`VercelAIInstrumentor\` into its existing \`instrumentations\` list instead of creating a second instance. Preserve other explicit instrumentors that cover genuinely separate direct-SDK calls; do not add a provider instrumentor solely for calls made through Vercel AI SDK.
136
-
137
- If the application already registers its own compatible \`@ai-sdk/otel\` integration, construct \`VercelAIInstrumentor({ autoRegisterAISDKTelemetry: false })\` so Respan translates the spans without claiming a second registration. For Next.js, follow the integration doc's root \`instrumentation.ts\` \`register()\` placement and \`serverExternalPackages\` configuration; keep tracing on the server runtime, not an Edge or browser path.
138
-
139
- Also enable Vercel telemetry on every AI SDK operation. Use the option that matches the installed AI SDK major version:
140
-
141
- \`\`\`typescript
142
- // AI SDK 7
143
- telemetry: { isEnabled: true }
144
-
145
- // AI SDK 4 through 6
146
- experimental_telemetry: { isEnabled: true }
147
- \`\`\`
148
-
149
- Do **not** add decorators. For non-Vercel Auto setups, do not add a framework instrumentor. For Vercel, keep the required \`VercelAIInstrumentor\` and go straight to **Verify**.
150
-
151
- ---
152
-
153
- ### Full path
154
-
155
- The rest of this section is the **Full** path — follow it only if the user chose Full. It adds an explicit framework instrumentor and/or decorators on top of the core SDK.
156
-
157
- #### 1. Analyze the Project
158
-
159
- **1a. Detect language and package manager:**
160
- - Check \`package.json\` (JS/TS) or \`pyproject.toml\` / \`requirements.txt\` (Python)
161
- - Detect package manager from lock files
162
-
163
- **1b. Detect libraries in priority order:**
164
-
165
- Check higher-priority categories first. If a match is found, use that instrumentation — do NOT also add lower-level SDK instrumentation.
166
-
167
- **Priority 1 — Agent Frameworks & High-Level SDKs:**
168
-
169
- | Library | Python package | JS/TS package | Respan instrumentation (Python) | Respan instrumentation (JS/TS) | Docs |
170
- |---------|---------------|---------------|--------------------------------|-------------------------------|------|
171
- | Vercel AI SDK | — | \`ai\` | — | \`@respan/instrumentation-vercel\` | [docs](https://respan.ai/docs/integrations/vercel-ai-sdk.md) |
172
- | OpenAI Agents SDK | \`openai-agents\` | \`@openai/agents\` | \`respan-instrumentation-openai-agents\` | \`@respan/instrumentation-openai-agents\` | [docs](https://respan.ai/docs/integrations/openai-agents-sdk.md) |
173
- | Claude Agent SDK | \`claude-agent-sdk\` | — | \`respan-instrumentation-claude-agent-sdk\` | — | [docs](https://respan.ai/docs/integrations/claude-agents-sdk.md) |
174
- | Pi coding agent | — | \`@earendil-works/pi-coding-agent\` | — | \`@respan/instrumentation-pi\` | [README](https://github.com/RespanAI/respan/tree/main/javascript-sdks/instrumentations/respan-instrumentation-pi) |
175
- | Pydantic AI | \`pydantic-ai\` | — | \`respan-instrumentation-pydantic-ai\` | — | [docs](https://respan.ai/docs/integrations/pydantic-ai.md) |
176
- | LangChain | \`langchain\` | \`langchain\` | \`respan-instrumentation-langchain\` | \`@respan/instrumentation-langchain\` | [docs](https://respan.ai/docs/integrations/langchain.md) |
177
- | LangGraph | \`langgraph\` | \`@langchain/langgraph\` | \`respan-instrumentation-langchain\` | \`@respan/instrumentation-langchain\` | [docs](https://respan.ai/docs/integrations/langgraph.md) |
178
- | CrewAI | \`crewai\` | — | \`respan-instrumentation-crewai\` | — | [docs](https://respan.ai/docs/integrations/crewai.md) |
179
- | LlamaIndex | \`llama-index\` | — | \`respan-instrumentation-llama-index\` | — | [docs](https://respan.ai/docs/integrations/llama-index.md) |
180
- | Haystack | \`haystack-ai\` | — | \`respan-instrumentation-haystack\` | — | [docs](https://respan.ai/docs/integrations/haystack.md) |
181
- | Mastra | — | \`mastra\` | — | via OTEL | [docs](https://respan.ai/docs/integrations/mastra.md) |
182
- | Google ADK | \`google-adk\` | — | \`respan-instrumentation-google-adk\` | — | [docs](https://respan.ai/docs/integrations/google-adk.md) |
183
-
184
- If a Priority 1 framework is found, use its instrumentation. Do NOT also add Priority 2 instrumentation for the same provider.
185
-
186
- For direct Vercel AI SDK usage, the explicit \`VercelAIInstrumentor\` and per-operation telemetry option are required in both Auto and Full. Never replace them with core-only \`new Respan()\` setup, and never stack them with a higher-level integration that already owns AI SDK telemetry.
187
-
188
- **LangChain / LangGraph setup differs by language** (one package covers both frameworks: \`respan-instrumentation-langchain\` / \`@respan/instrumentation-langchain\`). In **Python**, pass \`LangChainInstrumentor()\` to \`Respan(instrumentations=[...])\` — it patches the LangChain and LangGraph callback managers globally on init, so every chain, graph, node, LLM, tool, and retriever run is traced **automatically with no per-call setup**. \`add_respan_callback(config=...)\` is optional and only labels a run with a name, tags, or metadata. In **TypeScript**, create a \`LangChainInstrumentor\`, pass it to \`new Respan({ instrumentations: [...] })\`, **and** attach \`instrumentor.addCallback(...)\` to the **outermost** chain or graph invocation (\`.invoke()\` / \`.stream()\`, not inside a node — LangChain/LangGraph propagate it to nested runs); the TypeScript instrumentor does **not** patch globally, so without the callback no spans are emitted. (The other Priority-1 frameworks use the standard \`Respan(instrumentations=[XInstrumentor()])\` plugin pattern.)
189
-
190
- **Pi coding agent (TypeScript only)** has no global patch point, so the instrumentor must be wired into the session. Create \`new PiInstrumentor()\` from \`@respan/instrumentation-pi\`, pass it to \`new Respan({ instrumentations: [...] })\`, and either pass \`instrumentor.extension\` via \`new DefaultResourceLoader({ cwd: process.cwd(), agentDir: getAgentDir(), extensionFactories: [instrumentor.extension] })\` (pi requires \`cwd\` and \`agentDir\`; \`getAgentDir\` is exported by \`@earendil-works/pi-coding-agent\`) before \`createAgentSession(...)\`, or call \`instrumentor.attach(session)\` on an existing \`AgentSession\`. By default every run of a pi session joins one multi-root trace whose id is derived from the pi session id (\`traceScope: "session"\`), so a session resumed across processes and pauses stays one trace; \`traceScope: "run"\` makes each agent run its own trace instead. All runs of a pi session share its thread id either way. For always-on agent fleets, pair it with a local OpenTelemetry Collector persistent queue (see Delivery Guarantees below) so spans survive Respan outages and restarts without a local journal. For the interactive \`pi\` CLI, run \`respan integrate pi\` instead — it installs the \`@respan/instrumentation-pi\` pi package (a pi extension) and writes a non-secret \`respan.json\` (\`--trace-scope run\` for one trace per prompt instead of one per session).
191
-
192
- **Priority 2 — Direct LLM SDKs** (only if no P1 framework covers this provider):
193
-
194
- The integrations listed for each language are **auto-instrumented** with \`Respan()\` / \`new Respan()\`. The application still provides the provider SDK; the matching first-party instrumentation adapter is bundled by the Respan facade. A dash means that language currently requires an explicit instrumentation package and instrumentor.
195
-
196
- | Library | Python SDK (auto) | JS/TS SDK (auto) | Docs |
197
- |---------|-------------------|------------------|------|
198
- | OpenAI SDK | \`openai\` | \`openai\` | [docs](https://respan.ai/docs/integrations/openai-sdk.md) |
199
- | Anthropic SDK | \`anthropic\` | \`@anthropic-ai/sdk\` | [docs](https://respan.ai/docs/integrations/anthropic.md) |
200
- | Azure OpenAI | \`openai\` (Azure config) | \`openai\` (Azure client) | [docs](https://respan.ai/docs/integrations/providers/azure.md) |
201
- | Google Vertex AI | \`google-cloud-aiplatform\` | \`@google-cloud/vertexai\` | [docs](https://respan.ai/docs/integrations/vertex-ai.md) |
202
- | Google GenAI | \`google-genai\` | — | [docs](https://respan.ai/docs/integrations/google-genai.md) |
203
- | AWS Bedrock | \`boto3\` | \`@aws-sdk/client-bedrock-runtime\` | [docs](https://respan.ai/docs/integrations/aws-bedrock.md) |
204
- | Cohere | — | \`cohere-ai\` | [docs](https://respan.ai/docs/integrations/providers/cohere.md) |
205
- | Together AI | \`together\` | \`together-ai\` | [docs](https://respan.ai/docs/integrations/together-ai.md) |
206
- | OpenRouter | — | \`@openrouter/sdk\` | [docs](https://respan.ai/docs/integrations/providers/openrouter.md) |
207
- | Writer | — | \`writer-sdk\` | [docs](https://respan.ai/docs/integrations/writer.md) |
208
- | Ollama | \`ollama\` | — | [docs](https://respan.ai/docs/integrations/ollama.md) |
209
-
210
- **Note:** LLM wrappers such as Python LiteLLM stay explicit-only to avoid overlapping provider spans. See the [LiteLLM guide](https://respan.ai/docs/integrations/litellm.md) for its explicit setup.
211
-
212
- **1c. Read the actual code and understand the workflow:**
213
-
214
- This is the most important step. Read the entrypoint and all files that make LLM calls. Map out:
215
-
216
- - What is the **overall workflow**? (e.g. "user sends question → retrieve context → generate answer → format response")
217
- - What are the **individual steps/tasks**? (e.g. "embed query", "search DB", "call GPT", "parse output")
218
- - Are there **agent loops**? (e.g. a loop that calls tools until done)
219
- - Are there **tool calls**? (e.g. functions the LLM invokes)
220
-
221
- #### 2. Propose an Implementation Plan
222
-
223
- Present the user with a concrete plan before making any changes. The plan should include:
224
-
225
- **a) Packages to install** — core SDK + instrumentation package (with exact versions)
226
-
227
- **b) Initialization code** — where to add it (which file, which line)
228
-
229
- **c) Workflow structure** — how to wrap the existing code:
230
-
231
- For **agent frameworks** (Priority 1): The framework instrumentation auto-captures the workflow structure. Usually just need init code, no manual wrapping needed. Fetch and follow the integration doc.
232
-
233
- For **direct LLM SDKs** (Priority 2): Individual LLM calls will be auto-traced as flat spans. Propose wrapping the logical workflow with Respan decorators/wrappers to get structured nested traces:
234
-
235
- TypeScript example:
236
- \`\`\`typescript
237
- // Before: flat traces — each LLM call is an isolated span
238
- const outline = await openai.chat.completions.create({...});
239
- const draft = await openai.chat.completions.create({...});
240
-
241
- // After: structured traces — nested spans showing the workflow
242
- const result = await withWorkflow({ name: "write_article" }, async () => {
243
- const outline = await withTask({ name: "generate_outline" }, async () => {
244
- return await openai.chat.completions.create({...});
245
- });
246
- const draft = await withTask({ name: "write_draft" }, async () => {
247
- return await openai.chat.completions.create({...});
248
- });
249
- return draft;
250
- });
251
- \`\`\`
252
-
253
- Python example:
254
- \`\`\`python
255
- # Before: flat traces
256
- outline = client.chat.completions.create(...)
257
- draft = client.chat.completions.create(...)
258
-
259
- # After: structured traces
260
- @workflow(name="write_article")
261
- def write_article(topic):
262
- outline = generate_outline(topic)
263
- return write_draft(outline)
264
-
265
- @task(name="generate_outline")
266
- def generate_outline(topic):
267
- return client.chat.completions.create(...)
268
-
269
- @task(name="write_draft")
270
- def write_draft(outline):
271
- return client.chat.completions.create(...)
272
- \`\`\`
273
-
274
- Within Full, pick what the app needs (they can combine):
275
- - **Framework detected** → add its **explicit instrumentor** (or the LangChain callback). The framework auto-captures its agent / tool / chain structure — usually no manual wrapping needed.
276
- - **Direct LLM SDK, or you want structure over your own orchestration code** → add **decorators** (\`@workflow\` / \`@task\`) to wrap the logical workflow into nested spans.
277
-
278
- (If the user only wanted flat spans with zero code changes, that's the **Auto** path — go back to it.)
279
-
280
- When adding decorators:
281
- - **If multiple independent workflows are detected** (e.g. \`writeArticle()\`, \`summarizeDoc()\`, \`classifyEmail()\`), list them and ask which ones to instrument. Don't assume all of them.
282
- - **Show the user what the trace will look like** — describe the span hierarchy:
283
- \`\`\`
284
- workflow: write_article
285
- ├── task: generate_outline
286
- │ └── llm: openai.chat (auto-captured)
287
- └── task: write_draft
288
- └── llm: openai.chat (auto-captured)
289
- \`\`\`
290
-
291
- Wait for user confirmation before proceeding.
292
-
293
- #### 3. Implement
294
-
295
- **a) Install packages:**
296
-
297
- For direct LLM SDKs (Priority 2) — just the core SDK:
298
- \`\`\`bash
299
- # Python
300
- pip install respan-ai
301
-
302
- # TypeScript
303
- npm install @respan/respan
304
- \`\`\`
305
-
306
- For agent frameworks (Priority 1) — also install the instrumentor. Check the docs link in the table above for the exact packages.
307
-
308
- **b) Add initialization code** — at the top of the entrypoint, before any LLM client is created:
309
-
310
- For **direct LLM SDKs** (auto-instrumented):
311
- \`\`\`python
312
- # Python
313
- from respan import Respan
314
- Respan()
315
- \`\`\`
316
- \`\`\`typescript
317
- // TypeScript
318
- import { Respan } from "@respan/respan";
319
- const respan = new Respan();
320
- await respan.initialize();
321
- \`\`\`
322
-
323
- For **agent frameworks** (explicit instrumentor — fetch the docs URL from the table for the exact pattern):
324
- \`\`\`python
325
- # Python example (OpenAI Agents)
326
- from respan import Respan
327
- from respan_instrumentation_openai_agents import OpenAIAgentsInstrumentor
328
- Respan(instrumentations=[OpenAIAgentsInstrumentor()])
329
- \`\`\`
330
- \`\`\`typescript
331
- // TypeScript example (OpenAI Agents)
332
- import { Respan } from "@respan/respan";
333
- import { OpenAIAgentsInstrumentor } from "@respan/instrumentation-openai-agents";
334
- const respan = new Respan({ instrumentations: [new OpenAIAgentsInstrumentor()] });
335
- await respan.initialize();
336
- \`\`\`
337
-
338
- **c) Add workflow wrappers** — if the user chose structured traces in the plan.
339
-
340
- ### Verify (final test — always run this, both paths)
341
-
342
- As the final step — for **both** Auto and Full — **run the user's program once with a small request** and confirm tracing works end to end:
343
-
344
- - The app runs without errors
345
- - A trace appears at https://platform.respan.ai or via \`respan traces list --limit 5\`
346
- - If wrappers were added, the trace shows the expected nested span hierarchy
347
-
348
- If no trace appears, the instrumentation isn't taking effect — debug (init placement, missing \`flush()\`, wrong entrypoint) and re-run before declaring setup done.
349
-
350
- ---
351
-
352
- ## Reference
353
-
354
- Advanced tracing configuration: decorators, context propagation, processors, and span attributes.
355
-
356
- ### Decorators
357
-
358
- Wrap functions to create structured span hierarchies. All decorators share the same signature.
359
-
360
- | Decorator | Purpose |
361
- |-----------|---------|
362
- | \`@workflow\` / \`withWorkflow\` | Root span — top-level pipeline |
363
- | \`@task\` / \`withTask\` | Step within a workflow |
364
- | \`@agent\` / \`withAgent\` | Agent loop span |
365
- | \`@tool\` / \`withTool\` | Tool/function call span |
366
-
367
- #### Python
368
-
369
- \`\`\`python
370
- from respan import Respan, workflow, task, agent, tool
371
-
372
- Respan()
373
-
374
- @workflow(name="write_article")
375
- def write_article(topic: str):
376
- outline = generate_outline(topic)
377
- return write_draft(outline)
378
-
379
- @task(name="generate_outline")
380
- def generate_outline(topic: str):
381
- return client.chat.completions.create(...)
382
-
383
- @task(name="write_draft")
384
- def write_draft(outline: str):
385
- return client.chat.completions.create(...)
386
- \`\`\`
387
-
388
- #### TypeScript
389
-
390
- \`\`\`typescript
391
- import { Respan, withWorkflow, withTask } from "@respan/respan";
392
-
393
- const respan = new Respan();
394
- await respan.initialize();
395
-
396
- const result = await withWorkflow({ name: "write_article" }, async () => {
397
- const outline = await withTask({ name: "generate_outline" }, async () => {
398
- return await client.chat.completions.create({...});
399
- });
400
- return await withTask({ name: "write_draft" }, async () => {
401
- return await client.chat.completions.create({...});
402
- });
403
- });
404
- \`\`\`
405
-
406
- #### Decorator Parameters
407
-
408
- \`\`\`python
409
- @workflow(
410
- name="my_workflow", # Display name (default: function name)
411
- version=1, # Version number
412
- processors="my_processor", # Route to specific processor(s)
413
- sample_rate=0.5, # 0.0-1.0, fraction of spans exported
414
- export_filter={ # Conditional export
415
- "metadata.env": {"operator": "", "value": "production"}
416
- },
417
- )
418
- \`\`\`
419
-
420
- All parameters are optional. Without any, the function name is used as the span name.
421
-
422
- #### Async Support
423
-
424
- Decorators work with \`async def\`, generators, and async generators automatically.
425
-
426
- ### Context Propagation
427
-
428
- Attach attributes to **all spans** within a scope, including auto-instrumented LLM calls.
429
-
430
- #### propagate_attributes
431
-
432
- \`\`\`python
433
- from respan import Respan, propagate_attributes
434
-
435
- Respan()
436
-
437
- with propagate_attributes(
438
- customer_identifier="user_123",
439
- thread_identifier="conv_abc",
440
- environment="production",
441
- metadata={"plan": "pro", "team": "search"},
442
- ):
443
- # All spans here (including auto-instrumented OpenAI calls) get these attributes
444
- result = run_pipeline()
445
- \`\`\`
446
-
447
- ##### Available attributes
448
-
449
- | Attribute | Description |
450
- |-----------|-------------|
451
- | \`customer_identifier\` | User/customer ID — enables per-user analytics |
452
- | \`customer_email\` | Customer email |
453
- | \`customer_name\` | Customer display name |
454
- | \`thread_identifier\` | Conversation thread ID — groups related calls |
455
- | \`custom_identifier\` | Indexed custom identifier for fast queries |
456
- | \`group_identifier\` | Group related traces together |
457
- | \`environment\` | Environment name (e.g. \`"production"\`, \`"staging"\`) |
458
- | \`metadata\` | Dict of custom key-value pairs (merged in nested contexts) |
459
- | \`prompt\` | Dict with \`prompt_id\` and \`variables\` for prompt logging |
460
-
461
- ##### TypeScript
462
-
463
- \`\`\`typescript
464
- const result = await respan.propagateAttributes(
465
- {
466
- customerIdentifier: "user_123",
467
- threadIdentifier: "conv_abc",
468
- metadata: { plan: "pro" },
469
- },
470
- async () => {
471
- return await runPipeline();
472
- }
473
- );
474
- \`\`\`
475
-
476
- ##### Nesting behavior
477
-
478
- - Nested \`propagate_attributes\` calls merge with the outer context
479
- - \`metadata\` dicts are merged (not replaced)
480
- - For duplicate keys, inner values override outer
481
- - Async-safe via \`contextvars\`
482
-
483
- #### respan_span_attributes
484
-
485
- Attaches attributes to the **current active span only** (not auto-instrumented child spans):
486
-
487
- \`\`\`python
488
- from respan import respan_span_attributes
489
-
490
- with respan_span_attributes({
491
- "customer_identifier": "user-123",
492
- "metadata": {"priority": "high"},
493
- }):
494
- pass
495
- \`\`\`
496
-
497
- Use \`propagate_attributes\` when you need attributes on all nested spans (most common). Use \`respan_span_attributes\` when you only want to tag the current span.
498
-
499
- ### Imperative Span Creation
500
-
501
- For runtime-determined span names, use the client API instead of decorators:
502
-
503
- \`\`\`python
504
- from respan import get_client
505
-
506
- client = get_client()
507
-
508
- with client.start_span(name="dynamic_step", kind="task") as span:
509
- result = do_work()
510
- # span is automatically closed and exported
511
- \`\`\`
512
-
513
- ### Updating the Current Span
514
-
515
- Add attributes or change status on the active span at runtime:
516
-
517
- \`\`\`python
518
- from respan import get_client
519
-
520
- client = get_client()
521
-
522
- # Inside a decorated function or start_span context:
523
- client.update_current_span(
524
- respan_params={
525
- "customer_identifier": "user_123",
526
- "metadata": {"step": "final"},
527
- },
528
- name="renamed_span",
529
- )
530
-
531
- # Add events
532
- client.add_event("checkpoint_reached", attributes={"items": 42})
533
-
534
- # Record exceptions (span continues)
535
- try:
536
- risky_operation()
537
- except Exception as e:
538
- client.record_exception(e)
539
- \`\`\`
540
-
541
- ### Custom Processors
542
-
543
- Route spans to multiple destinations with filtering:
544
-
545
- \`\`\`python
546
- from respan_tracing import RespanTelemetry
547
-
548
- telemetry = RespanTelemetry(api_key="...", base_url="...")
549
-
550
- # Add a second exporter for debug logging
551
- from opentelemetry.sdk.trace.export import ConsoleSpanExporter
552
-
553
- telemetry.add_processor(
554
- exporter=ConsoleSpanExporter(),
555
- name="debug",
556
- filter_fn=lambda span: span.attributes.get("environment") == "debug",
557
- )
558
- \`\`\`
559
-
560
- Route specific spans to a processor using the \`processors\` parameter on decorators:
561
-
562
- \`\`\`python
563
- @task(name="sensitive_step", processors="debug")
564
- def sensitive_step():
565
- ...
566
- \`\`\`
567
-
568
- ### Flush
569
-
570
- Always call \`flush()\` before process exit to ensure all spans are exported:
571
-
572
- \`\`\`python
573
- respan = Respan()
574
- # ... your code ...
575
- respan.flush()
576
- \`\`\`
577
-
578
- In serverless/Lambda: call \`flush()\` at the end of every handler invocation.
579
-
580
- ### Delivery Guarantees
581
-
582
- The SDK batches spans in memory and the OTLP exporter retries 429/502/503/504 with backoff; it never writes to disk. Spans still queued during a long outage or a process crash are lost — the same trade-off every LLM observability SDK makes. For long-running production agents where that is not acceptable, run the packaged Respan Collector next to the application: \`respan collector start\` (Docker if available, otherwise the pinned \`otelcol-contrib\` binary) or \`docker compose -f collector/docker-compose.yaml up -d\` from the monorepo's \`collector/\` directory, then set \`RESPAN_BASE_URL=http://127.0.0.1:4318\`. It is the upstream OpenTelemetry Collector with a persistent, bounded queue (\`file_storage\` write-ahead log capped by \`RESPAN_COLLECTOR_MAX_SIZE_BYTES\`, \`RESPAN_COLLECTOR_QUEUE_SIZE\`, retries with \`max_elapsed_time: 0\`, uncompressed JSON export because Respan's ingest does not inflate gzip) receiving on \`/api/v2/traces\`, the path the SDK posts to. Never build a local journal into the application itself; alert on \`otelcol_exporter_enqueue_failed_spans\` (drops) and watch \`otelcol_exporter_queue_size\`. \`respan collector status\` prints those counters.
583
-
584
- ### Trace Hierarchy Example
585
-
586
- \`\`\`
587
- workflow: handle_request
588
- +-- task: classify_intent
589
- | +-- llm: openai.chat (auto-captured)
590
- +-- agent: support_agent
591
- | +-- tool: lookup_order
592
- | +-- llm: openai.chat (auto-captured)
593
- | +-- tool: process_refund
594
- +-- task: generate_response
595
- +-- llm: openai.chat (auto-captured)
596
- \`\`\`
597
-
598
- LLM calls within decorated functions are automatically nested as child spans.
599
- `;
600
- export const GATEWAY_MD = `# Gateway
601
-
602
- Set up and use the Respan **gateway** — routing an app's LLM calls through the Respan
603
- proxy for logging, caching, key management, fallbacks, and model switching.
604
-
605
- This file covers **both** the setup steps and the feature reference. Gateway is a
606
- separate setup from tracing — do one at a time, never both in the same pass. Gateway
607
- setup does **not** install instrumentation packages; it repoints the LLM client's base
608
- URL at the Respan proxy and applies the framework's specific wiring.
609
-
610
- Docs: \`https://respan.ai/docs/documentation/features/gateway/gateway-quickstart.md\`
611
- Full docs index: \`https://www.respan.ai/docs/llms.txt\`
612
-
613
- ---
614
-
615
- ## Setup
616
-
617
- Use this section when the user asks to set up the Respan gateway.
618
-
619
- ### Hard Rules
620
-
621
- - **Interactive mode:** Ask the user questions when you need input. Do not assume.
622
- - **Only add Respan wiring.** Do not refactor or modify unrelated code.
623
- - **The gateway serves only the completion / text-generation operation**, exposed per provider:
624
- - OpenAI-compatible → \`https://api.respan.ai/api/chat/completions\`
625
- - Anthropic Messages → \`https://api.respan.ai/api/anthropic/\`
626
- - Google Gemini → \`https://api.respan.ai/api/google/gemini/...\`
627
-
628
- **Use the exact base path from the per-SDK gateway doc you fetch in Step 4** — it is authoritative. Do **NOT** repoint clients used for any other operation — embeddings, moderation, images, audio/speech, assistants, batch, fine-tuning — they are not supported and will fail (e.g. 404). Leave those untouched.
629
- - **Use chat completions, not the Responses API.** The gateway serves chat completions (\`/api/chat/completions\`), not the OpenAI Responses API (\`/api/responses\`). SDKs that *default* to the Responses API are still fully supported — you just switch them to chat-completions mode, per their per-SDK doc:
630
- - **OpenAI Agents SDK** — Python: \`set_default_openai_client(AsyncOpenAI(base_url=..., api_key=os.environ["RESPAN_API_KEY"]))\` + \`set_default_openai_api("chat_completions")\` (TS: \`setDefaultOpenAIClient(...)\` + \`setOpenAIAPI("chat_completions")\`).
631
- - **Vercel AI SDK / \`@ai-sdk/openai\`** — use \`provider.chat(model)\`, not the default \`provider(model)\` (which targets the Responses API).
632
- - **Scope the wiring — do not blanket-rewrite.** Wire only the client(s) that make the completion call you are setting up. Do **NOT** repoint every client instance in the repo (especially in example/monorepo projects with many independent scripts).
633
- - **Do NOT install instrumentation packages.** Gateway routing does not use them. (That is tracing setup — see \`tracing.md\`.)
634
- - **Do not guess APIs or doc slugs.** Use only the patterns from the live integration doc you fetch, and the exact slug from the map below.
635
- - **Pin exact versions** if you install a framework SDK that is genuinely missing.
636
- - **If the gateway is already wired up, do not duplicate work.** Check for an existing \`base_url\`/\`baseURL\` pointing at \`api.respan.ai\` first.
637
- - **Read the code before proposing changes.** Find where the client is actually instantiated.
638
-
639
- The API key is stored in \`.env\` as \`RESPAN_API_KEY\`. Gateway base URL: \`https://api.respan.ai/api\` (the proxy completions path is \`https://api.respan.ai/api/chat/completions\`).
640
-
641
- ### 1. Detect
642
-
643
- Identify:
644
- - **Language & package manager** — \`package.json\` (JS/TS), \`pyproject.toml\` / \`requirements.txt\` (Python), \`Gemfile\` (Ruby). Detect the package manager from lock files.
645
- - **LLM library / framework in use** — match the project's dependencies against the **Detection → slug map** below.
646
-
647
- ### 2. Map to slug
648
-
649
- Match the detected package(s) against the map. **Use the slug from the map verbatim — never auto-derive a slug from a display name** (the kebab-casing is irregular).
650
-
651
- Apply the **priority rule:** a high-level framework wins over the raw SDK beneath it.
652
- For example, a project with both \`crewai\` and \`openai\` maps to \`crew-ai\` (not \`open-ai-sdk\`),
653
- because their gateway wiring differs. **If two frameworks match, ask the user which to configure.**
654
-
655
- #### Detection → slug map
656
-
657
- URL pattern: \`https://respan.ai/docs/integrations/gateway/<slug>.md\`
658
-
659
- | Detected package(s) | Slug |
660
- |---|---|
661
- | \`@openai/agents\` / \`openai-agents\` | \`open-ai-agents\` |
662
- | \`claude-agent-sdk\` | \`claude-agent-sdk\` |
663
- | \`ai\` (Vercel AI SDK) | \`vercel-ai-sdk\` |
664
- | \`pydantic-ai\` | \`pydantic-ai\` |
665
- | \`crewai\` | \`crew-ai\` |
666
- | \`haystack-ai\` | \`haystack\` |
667
- | \`langchain\` | \`lang-chain\` |
668
- | \`llama-index\` | \`llama-index\` |
669
- | \`autogen\` / \`autogen-agentchat\` | \`auto-gen\` |
670
- | \`dspy\` / \`dspy-ai\` | \`ds-py\` |
671
- | \`google-adk\` | \`google-adk\` |
672
- | \`openai\` | \`open-ai-sdk\` |
673
- | \`anthropic\` / \`@anthropic-ai/sdk\` | \`anthropic-sdk\` |
674
- | \`google-genai\` / \`@google/genai\` | \`google-gen-ai\` |
675
- | \`litellm\` | \`lite-llm\` |
676
- | \`ruby_llm\` (gem) | \`ruby-llm\` |
677
- | \`google-cloud-aiplatform\` | \`vertex-ai\` |
678
-
679
- > Namespace note: OpenAI, Anthropic, and Vertex each have both an SDK-level gateway page (\`open-ai-sdk\`, \`anthropic-sdk\`, \`vertex-ai\`) and a \`model-providers/*\` page. The SDK-level slugs above are the correct targets for this map.
680
-
681
- ### 3. Confirm
682
-
683
- Show the user what was detected and which doc will be used. Wait for confirmation.
684
-
685
- > Detected:
686
- > - Language: [TypeScript/Python/Ruby]
687
- > - Framework / SDK: [e.g. OpenAI Agents]
688
- > - Gateway doc: https://respan.ai/docs/integrations/gateway/open-ai-agents.md
689
- >
690
- > Set up the gateway for this? (yes/no)
691
-
692
- ### 4. Fetch the live doc
693
-
694
- Fetch \`https://respan.ai/docs/integrations/gateway/<slug>.md\` for the chosen slug.
695
- Use the slug from the map verbatim.
696
-
697
- If a pinned URL fails to resolve, that signals the **map is stale** and the table should be
698
- updated — it does **not** mean you should guess an alternative slug.
699
-
700
- ### 5. Read the project's client code
701
-
702
- Find where the LLM client / \`base_url\` is instantiated, and identify **which client(s) make
703
- completion / text-generation calls** (chat completions, Anthropic \`messages.create\`, Gemini
704
- \`generate_content\`) — that is the only path you repoint. This is the step that determines the
705
- correct wiring — do not skip it.
706
-
707
- If the project has multiple clients or many scripts (e.g. an examples repo), repoint **only**
708
- the completion ones. Leave clients that call embeddings, moderation, images, audio,
709
- assistants, batch, or fine-tuning exactly as they are — the gateway does not serve them.
710
-
711
- ### 6. Apply the framework-specific wiring
712
-
713
- Apply the exact pattern from the fetched doc:
714
-
715
- - Ensure \`RESPAN_API_KEY\` is set (it is in \`.env\`).
716
- - Repoint \`base_url\` / \`baseURL\` to the gateway path from the fetched doc (e.g. \`https://api.respan.ai/api\` for OpenAI-compatible, \`…/api/anthropic/\` for Anthropic) — **only on the completion client(s)** identified in step 5, not on every client in the project.
717
- - Apply the framework's exact wiring — e.g. OpenAI Agents requires \`set_default_openai_client(AsyncOpenAI(base_url=...))\`, **not** env vars.
718
- - Install the framework SDK only if it is genuinely missing.
719
- - **Do NOT install instrumentation packages.**
720
-
721
- ### 7. Verify (final test — always run this)
722
-
723
- As the final step, **run the user's program once with a small request** through the
724
- configured gateway and confirm it works:
725
-
726
- \`\`\`bash
727
- respan logs list --limit 5
728
- \`\`\`
729
-
730
- Confirm:
731
- - The app runs without errors
732
- - The request appears in the gateway logs (via the CLI above, or the Respan platform at https://platform.respan.ai)
733
-
734
- If no log entry appears, the request is not actually routing through the gateway — debug
735
- the \`base_url\`/wiring and re-run before declaring setup done.
736
-
737
- ---
738
-
739
- ## Reference
740
-
741
- ### Wiring Patterns
742
-
743
- Point the LLM client's \`base_url\` at the Respan gateway. The Respan API key authenticates both the proxy and tracing.
744
-
745
- #### Python (OpenAI SDK)
746
-
747
- \`\`\`python
748
- import os
749
- from openai import OpenAI
750
-
751
- client = OpenAI(
752
- api_key=os.environ["RESPAN_API_KEY"],
753
- base_url=os.getenv("RESPAN_BASE_URL", "https://api.respan.ai/api"),
754
- )
755
-
756
- response = client.chat.completions.create(
757
- model="gpt-4o-mini",
758
- messages=[{"role": "user", "content": "Hello!"}],
759
- )
760
- \`\`\`
761
-
762
- #### TypeScript (OpenAI SDK)
763
-
764
- \`\`\`typescript
765
- import OpenAI from "openai";
766
-
767
- const client = new OpenAI({
768
- apiKey: process.env.RESPAN_API_KEY,
769
- baseURL: process.env.RESPAN_BASE_URL || "https://api.respan.ai/api",
770
- });
771
-
772
- const response = await client.chat.completions.create({
773
- model: "gpt-4o-mini",
774
- messages: [{ role: "user", content: "Hello!" }],
775
- });
776
- \`\`\`
777
-
778
- #### Python (Anthropic SDK)
779
-
780
- \`\`\`python
781
- import os
782
- import anthropic
783
-
784
- client = anthropic.Anthropic(
785
- api_key=os.environ["RESPAN_API_KEY"],
786
- base_url=os.getenv("RESPAN_BASE_URL", "https://api.respan.ai/api"),
787
- )
788
- \`\`\`
789
-
790
- #### Environment variables
791
-
792
- Set once, works everywhere:
793
-
794
- \`\`\`bash
795
- export RESPAN_API_KEY="your-key"
796
- export OPENAI_API_KEY="$RESPAN_API_KEY"
797
- export OPENAI_BASE_URL="https://api.respan.ai/api"
798
- \`\`\`
799
-
800
- ### Framework-Specific Gateway Setup
801
-
802
- #### CrewAI
803
-
804
- \`\`\`python
805
- import os
806
- os.environ["OPENAI_API_KEY"] = os.environ["RESPAN_API_KEY"]
807
- os.environ["OPENAI_BASE_URL"] = os.getenv("RESPAN_BASE_URL", "https://api.respan.ai/api")
808
-
809
- from crewai import Agent, Task, Crew
810
- # CrewAI uses the env vars automatically
811
- \`\`\`
812
-
813
- #### Haystack
814
-
815
- \`\`\`python
816
- import os
817
- os.environ["OPENAI_API_KEY"] = os.environ["RESPAN_API_KEY"]
818
- os.environ["OPENAI_BASE_URL"] = os.getenv("RESPAN_BASE_URL", "https://api.respan.ai/api")
819
-
820
- from haystack.components.generators import OpenAIGenerator
821
- # OpenAIGenerator picks up env vars automatically
822
- \`\`\`
823
-
824
- #### OpenAI Agents SDK
825
-
826
- \`\`\`python
827
- import os
828
- os.environ["OPENAI_API_KEY"] = os.environ["RESPAN_API_KEY"]
829
- os.environ["OPENAI_BASE_URL"] = os.getenv("RESPAN_BASE_URL", "https://api.respan.ai/api")
830
- \`\`\`
831
-
832
- #### Pydantic AI
833
-
834
- \`\`\`python
835
- import os
836
- os.environ["OPENAI_API_KEY"] = os.environ["RESPAN_API_KEY"]
837
- os.environ["OPENAI_BASE_URL"] = os.getenv("RESPAN_BASE_URL", "https://api.respan.ai/api")
838
-
839
- from pydantic_ai import Agent
840
- agent = Agent(model="openai:gpt-4o-mini")
841
- \`\`\`
842
-
843
- ### Model Switching
844
-
845
- The gateway supports 250+ models across providers. Use any model name:
846
-
847
- \`\`\`python
848
- # OpenAI models
849
- response = client.chat.completions.create(model="gpt-4o", ...)
850
-
851
- # Anthropic models (via OpenAI SDK!)
852
- response = client.chat.completions.create(model="claude-sonnet-4-20250514", ...)
853
-
854
- # Google models
855
- response = client.chat.completions.create(model="gemini-2.0-flash", ...)
856
-
857
- # Open-source models
858
- response = client.chat.completions.create(model="llama-3.1-70b", ...)
859
- \`\`\`
860
-
861
- No code changes needed — just change the model string. View all models at \`https://platform.respan.ai/platform/models\`.
862
-
863
- ### Advanced Features
864
-
865
- #### Fallback Models
866
-
867
- Automatic failover if the primary model fails:
868
-
869
- \`\`\`python
870
- response = client.chat.completions.create(
871
- model="gpt-4o",
872
- messages=[...],
873
- extra_body={
874
- "fallback_models": ["claude-sonnet-4-20250514", "gemini-2.0-flash"],
875
- },
876
- )
877
- \`\`\`
878
-
879
- #### Retries
880
-
881
- \`\`\`python
882
- response = client.chat.completions.create(
883
- model="gpt-4o",
884
- messages=[...],
885
- extra_body={
886
- "retries": 3,
887
- },
888
- )
889
- \`\`\`
890
-
891
- #### Caching
892
-
893
- Enable response caching to save cost on repeated calls:
894
-
895
- \`\`\`python
896
- response = client.chat.completions.create(
897
- model="gpt-4o",
898
- messages=[...],
899
- extra_body={
900
- "cache_enabled": True,
901
- "cache_ttl": 3600, # seconds
902
- },
903
- )
904
- \`\`\`
905
-
906
- #### Streaming
907
-
908
- Streaming works normally through the gateway:
909
-
910
- \`\`\`python
911
- stream = client.chat.completions.create(
912
- model="gpt-4o-mini",
913
- messages=[{"role": "user", "content": "Tell me a story"}],
914
- stream=True,
915
- )
916
- for chunk in stream:
917
- print(chunk.choices[0].delta.content, end="")
918
- \`\`\`
919
-
920
- #### Disable Logging
921
-
922
- For sensitive data, disable logging on specific requests:
923
-
924
- \`\`\`python
925
- response = client.chat.completions.create(
926
- model="gpt-4o",
927
- messages=[...],
928
- extra_body={
929
- "disable_log": True,
930
- },
931
- )
932
- \`\`\`
933
-
934
- ### Key Benefits
935
-
936
- - **Unified key management** — one Respan API key for all providers
937
- - **Automatic logging** — every request logged as a span
938
- - **Cost tracking** — per-request cost tracking across providers
939
- - **Caching** — cache repeated LLM calls to reduce cost and latency
940
- - **Fallbacks** — automatic failover between providers
941
- - **Model switching** — swap models without code changes
942
- - **~50-150ms added latency** — minimal overhead
943
- `;
944
- export const PROMPTS_MD = `# Prompts Reference
945
-
946
- Create, version, deploy, and use prompt templates from the Respan platform.
947
-
948
- Docs: \`https://respan.ai/docs/documentation/features/prompt-management/prompt-management-quickstart.md\`
949
-
950
- ---
951
-
952
- ## Overview
953
-
954
- Prompts are version-controlled templates stored on the Respan platform. The workflow is:
955
-
956
- 1. **Create** a prompt with a name
957
- 2. **Add versions** with messages, model config, and \`{{variables}}\`
958
- 3. **Deploy** the version you want live
959
- 4. **Fetch** the prompt in code using the API or MCP tools
960
- 5. **Iterate** — commit new versions and deploy without code changes
961
-
962
- ---
963
-
964
- ## Create a Prompt
965
-
966
- ### Via MCP
967
-
968
- \`\`\`
969
- Use create_prompt to create a prompt:
970
- name: "classify_intent"
971
- description: "Classifies user messages into intent categories"
972
-
973
- Then use create_prompt_version to add content:
974
- prompt_id: "<id from above>"
975
- model: "gpt-4o-mini"
976
- messages:
977
- - role: "system"
978
- content: "You are an intent classifier. Classify the user message into one of: question, complaint, request, feedback."
979
- - role: "user"
980
- content: "{{user_message}}"
981
- \`\`\`
982
-
983
- ### Via CLI
984
-
985
- \`\`\`bash
986
- respan prompts create --name "classify_intent"
987
- respan prompts versions <prompt-id>
988
- \`\`\`
989
-
990
- ---
991
-
992
- ## Variables
993
-
994
- Use \`{{variable_name}}\` in message content. Variables are filled at runtime.
995
-
996
- \`\`\`json
997
- {
998
- "messages": [
999
- {"role": "system", "content": "You are a {{role}} assistant."},
1000
- {"role": "user", "content": "{{user_input}}"}
1001
- ]
1002
- }
1003
- \`\`\`
1004
-
1005
- ### Jinja templates
1006
-
1007
- Prompts support Jinja2 syntax:
1008
-
1009
- - **Conditionals:** \`{% if context %}Use this context: {{context}}{% endif %}\`
1010
- - **JSON access:** \`{{ input.key }}\`
1011
- - **Filters:** \`{{ var | upper }}\`
1012
- - **Loops:** \`{% for item in items %}{{item}}{% endfor %}\`
1013
- - **Default values:** Set via the API or platform UI
1014
-
1015
- ---
1016
-
1017
- ## Versioning and Deployment
1018
-
1019
- Each prompt can have multiple versions. Only one version is **deployed** (live) at a time.
1020
-
1021
- ### Workflow
1022
-
1023
- 1. \`create_prompt_version\` — creates a new version (always starts as NOT deployed)
1024
- 2. Test the version in the playground or via API
1025
- 3. Deploy via platform UI when ready
1026
- 4. Code using \`prompt_id\` automatically gets the deployed version
1027
-
1028
- ### Version pinning
1029
-
1030
- \`\`\`python
1031
- # Always use the deployed version (default)
1032
- prompt = get_prompt(prompt_id="abc123")
1033
-
1034
- # Pin to a specific version
1035
- prompt = get_prompt(prompt_id="abc123", version=3)
1036
- \`\`\`
1037
-
1038
- ---
1039
-
1040
- ## Using Prompts in Code
1041
-
1042
- ### Python (via Gateway)
1043
-
1044
- Fetch the prompt config and pass to the LLM:
1045
-
1046
- \`\`\`python
1047
- import os
1048
- from openai import OpenAI
1049
-
1050
- client = OpenAI(
1051
- api_key=os.environ["RESPAN_API_KEY"],
1052
- base_url=os.getenv("RESPAN_BASE_URL", "https://api.respan.ai/api"),
1053
- )
1054
-
1055
- # The gateway resolves the prompt template and fills variables
1056
- response = client.chat.completions.create(
1057
- model="gpt-4o-mini",
1058
- messages=[], # Messages come from the prompt template
1059
- extra_body={
1060
- "prompt_id": "your-prompt-id",
1061
- "prompt_variables": {
1062
- "user_message": "I want to return my order",
1063
- },
1064
- },
1065
- )
1066
- \`\`\`
1067
-
1068
- ### Linking traces to prompts
1069
-
1070
- Use \`propagate_attributes\` to tag traces with the prompt used:
1071
-
1072
- \`\`\`python
1073
- from respan import propagate_attributes
1074
-
1075
- with propagate_attributes(
1076
- prompt={"prompt_id": "abc123", "variables": {"user_message": "Hello"}},
1077
- ):
1078
- response = client.chat.completions.create(...)
1079
- \`\`\`
1080
-
1081
- ---
1082
-
1083
- ## Prompt Composition
1084
-
1085
- Variables can reference other prompts (child prompts rendered first, injected as text).
1086
-
1087
- Configure in the platform UI by changing a variable's type from **Text** to **Prompt**.
1088
-
1089
- Max nesting depth: 2 (parent -> child).
1090
-
1091
- ---
1092
-
1093
- ## Structured Output (JSON Schema)
1094
-
1095
- Attach a JSON schema to a prompt version for structured responses:
1096
-
1097
- \`\`\`json
1098
- {
1099
- "response_format": {
1100
- "type": "json_schema",
1101
- "json_schema": {
1102
- "name": "extraction",
1103
- "strict": true,
1104
- "schema": {
1105
- "type": "object",
1106
- "properties": {
1107
- "intent": {"type": "string"},
1108
- "confidence": {"type": "number"}
1109
- },
1110
- "required": ["intent", "confidence"],
1111
- "additionalProperties": false
1112
- }
1113
- }
1114
- }
1115
- }
1116
- \`\`\`
1117
-
1118
- ---
1119
-
1120
- ## MCP Tools Reference
1121
-
1122
- | Action | Tool |
1123
- |--------|------|
1124
- | List all prompts | \`list_prompts\` |
1125
- | Get prompt details | \`get_prompt_detail(prompt_id)\` |
1126
- | Create a prompt | \`create_prompt(name, description)\` |
1127
- | Update name/description | \`update_prompt(prompt_id, name, description)\` |
1128
- | Create a version | \`create_prompt_version(prompt_id, messages, model, ...)\` |
1129
- | Update a version | \`update_prompt_version(prompt_id, version, ...)\` |
1130
- | List versions | \`list_prompt_versions(prompt_id)\` |
1131
- | Get version detail | \`get_prompt_version_detail(prompt_id, version)\` |
1132
-
1133
- ---
1134
-
1135
- ## CLI Commands
1136
-
1137
- \`\`\`bash
1138
- respan prompts list # List all prompts
1139
- respan prompts get <id> # Get prompt details
1140
- respan prompts create --name <name> # Create a prompt
1141
- respan prompts update <id> # Update a prompt
1142
- respan prompts versions <id> # List prompt versions
1143
- \`\`\`
1144
- `;
1145
- export const EVALS_MD = `# Evals Reference
1146
-
1147
- Build evaluation pipelines: datasets, evaluators, and experiments.
1148
-
1149
- Docs: \`https://respan.ai/docs/documentation/features/evals/concepts.md\`
1150
-
1151
- ---
1152
-
1153
- ## Overview
1154
-
1155
- Two evaluation modes:
1156
-
1157
- - **Offline** — build datasets, run experiments across prompt/model variants, compare scores
1158
- - **Online** — automatically score live production traffic (see [monitors.md](monitors.md))
1159
-
1160
- Offline eval workflow:
1161
-
1162
- \`\`\`
1163
- Create dataset -> Create evaluator -> Create experiment -> Run & compare
1164
- \`\`\`
1165
-
1166
- ---
1167
-
1168
- ## Datasets
1169
-
1170
- A dataset is a collection of test rows (input/output pairs) for experiments.
1171
-
1172
- ### Create from scratch
1173
-
1174
- \`\`\`
1175
- Use create_dataset:
1176
- name: "intent_classification_testset"
1177
- description: "Test cases for intent classifier"
1178
-
1179
- Then add rows with create_dataset_span:
1180
- dataset_id: "<id>"
1181
- input: '{"user_message": "I want a refund"}'
1182
- output: '{"intent": "refund_request"}'
1183
- \`\`\`
1184
-
1185
- ### Create from production traces
1186
-
1187
- Sample real spans from traced data:
1188
-
1189
- \`\`\`
1190
- 1. Use list_traces or list_logs to find relevant spans
1191
- 2. Use add_spans_to_dataset to add span IDs to a dataset:
1192
- dataset_id: "<id>"
1193
- span_ids: ["span_1", "span_2", "span_3"]
1194
- \`\`\`
1195
-
1196
- Or use the platform UI: Datasets -> Insert by sampling (filter, set sampling %, preview, confirm).
1197
-
1198
- ### CSV import
1199
-
1200
- Upload CSV via the platform UI. Columns map to prompt variables. Optional \`ideal_output\` column for expected outputs. Max 500 rows per import.
1201
-
1202
- ### MCP tools
1203
-
1204
- | Action | Tool |
1205
- |--------|------|
1206
- | Create dataset | \`create_dataset(name, description)\` |
1207
- | List datasets | \`list_datasets\` |
1208
- | Get dataset | \`get_dataset(dataset_id)\` |
1209
- | Update dataset | \`update_dataset(dataset_id, name, description)\` |
1210
- | Add a row manually | \`create_dataset_span(dataset_id, input, output)\` |
1211
- | Add existing spans | \`add_spans_to_dataset(dataset_id, span_ids)\` |
1212
- | List rows | \`list_dataset_spans(dataset_id)\` |
1213
- | Get a row | \`retrieve_dataset_span(dataset_id, log_id)\` |
1214
- | Update a row | \`update_dataset_span(dataset_id, log_id, ...)\` |
1215
-
1216
- ---
1217
-
1218
- ## Evaluators
1219
-
1220
- An evaluator scores LLM outputs. It is a workflow built from **graders** connected with conditions.
1221
-
1222
- ### Grader types
1223
-
1224
- | Type | How it works |
1225
- |------|-------------|
1226
- | **LLM grader** | A language model judges the output. Uses variables: \`{{output}}\`, \`{{input}}\`, \`{{expected_output}}\`, \`{{metadata}}\`, \`{{metrics}}\` |
1227
- | **Code grader** | A Python function returns a score. Signature: \`main(eval_inputs)\` where \`eval_inputs["output"]\` is required |
1228
- | **Human grader** | Team members review and score outputs manually |
1229
-
1230
- ### Score value types
1231
-
1232
- | Type | Use case |
1233
- |------|----------|
1234
- | \`numerical\` | Rating scales (e.g. 0-5) |
1235
- | \`boolean\` | Pass/fail checks |
1236
- | \`categorical\` | Multi-choice classifications |
1237
- | \`comment\` | Qualitative text feedback |
1238
-
1239
- ### Create an evaluator
1240
-
1241
- \`\`\`
1242
- Use create_evaluator:
1243
- name: "response_quality"
1244
- score_value_type: "numerical"
1245
- type: "llm"
1246
- description: "Scores response quality 1-5"
1247
- evaluator_slug: "response-quality"
1248
- \`\`\`
1249
-
1250
- For complex evaluators with grader workflows, conditions, and compute blocks — use the platform UI (Evaluators -> Create).
1251
-
1252
- ### Evaluator workflow blocks
1253
-
1254
- - **Markers:** \`Original input\`, \`Final result\` (entry/exit)
1255
- - **Graders:** LLM, Code, or Human scoring
1256
- - **Conditions:** \`If...Then...Else\` branching based on scores
1257
- - **Compute:** Average/weighted average of multiple grader scores
1258
- - **Metrics:** Built-in: completion tokens, cost, latency, model, prompt tokens, total tokens
1259
- - **Constants:** Fixed threshold values
1260
-
1261
- ### MCP tools
1262
-
1263
- | Action | Tool |
1264
- |--------|------|
1265
- | List evaluators | \`list_evaluators\` |
1266
- | Get evaluator | \`get_evaluator(evaluator_id)\` |
1267
- | Create evaluator | \`create_evaluator(name, score_value_type, ...)\` |
1268
- | Update evaluator | \`update_evaluator(evaluator_id, ...)\` |
1269
- | Run evaluator | \`run_evaluator(evaluator_id, dataset_id or log_ids)\` |
1270
-
1271
- ---
1272
-
1273
- ## Experiments
1274
-
1275
- An experiment runs evaluators across a dataset to compare prompt/model configurations.
1276
-
1277
- ### Workflow
1278
-
1279
- 1. Create a dataset with test rows
1280
- 2. Create evaluator(s) to score outputs
1281
- 3. Create an experiment linking dataset + evaluators
1282
- 4. Run the experiment — it generates outputs for each row and scores them
1283
- 5. Compare results across configurations
1284
-
1285
- ### Create an experiment
1286
-
1287
- \`\`\`
1288
- Use create_experiment:
1289
- name: "v2_vs_v3_comparison"
1290
- dataset_id: "<dataset-id>"
1291
- evaluator_slugs: ["response-quality", "hallucination-check"]
1292
- workflows:
1293
- - type: "prompt"
1294
- config: { prompt_id: "<prompt-v2-id>" }
1295
- - type: "prompt"
1296
- config: { prompt_id: "<prompt-v3-id>" }
1297
- \`\`\`
1298
-
1299
- ### Workflow types
1300
-
1301
- | Type | Description |
1302
- |------|-------------|
1303
- | \`prompt\` | Run a saved prompt template against dataset rows |
1304
- | \`completion\` | Run a model completion with config |
1305
- | \`custom\` | User-defined logic |
1306
-
1307
- ### MCP tools
1308
-
1309
- | Action | Tool |
1310
- |--------|------|
1311
- | List experiments | \`list_experiments\` |
1312
- | Get experiment | \`get_experiment(experiment_id)\` |
1313
- | Create experiment | \`create_experiment(name, dataset_id, ...)\` |
1314
- | List experiment spans | \`list_experiment_spans(experiment_id)\` |
1315
- | Get experiment span | \`get_experiment_span(experiment_id, log_id)\` |
1316
- | Update experiment span | \`update_experiment_span(experiment_id, log_id, ...)\` |
1317
- | Get summary stats | \`get_experiment_spans_summary(experiment_id, start_time, end_time)\` |
1318
-
1319
- ---
1320
-
1321
- ## End-to-End Example
1322
-
1323
- \`\`\`
1324
- Step 1: Create dataset
1325
- create_dataset(name="qa_testset")
1326
- create_dataset_span(dataset_id="...", input="What is Python?", output="A programming language")
1327
- create_dataset_span(dataset_id="...", input="Who made Rust?", output="Mozilla Research")
1328
-
1329
- Step 2: Create evaluator
1330
- create_evaluator(
1331
- name="accuracy_check",
1332
- score_value_type="boolean",
1333
- type="llm",
1334
- evaluator_slug="accuracy-check"
1335
- )
1336
-
1337
- Step 3: Create experiment
1338
- create_experiment(
1339
- name="gpt4o_vs_claude",
1340
- dataset_id="...",
1341
- evaluator_slugs=["accuracy-check"]
1342
- )
1343
-
1344
- Step 4: Review results
1345
- list_experiment_spans(experiment_id="...")
1346
- get_experiment_spans_summary(experiment_id="...", start_time="...", end_time="...")
1347
- \`\`\`
1348
-
1349
- ---
1350
-
1351
- ## CLI Commands
1352
-
1353
- \`\`\`bash
1354
- respan datasets list # List datasets
1355
- respan datasets create --name <name> # Create a dataset
1356
- respan datasets get <id> # Get dataset details
1357
- respan datasets spans <id> # List dataset spans
1358
- respan evaluators list # List evaluators
1359
- respan evaluators get <id> # Get evaluator details
1360
- respan evaluators create # Create an evaluator
1361
- respan evaluators run <id> # Run an evaluator
1362
- respan experiments list # List experiments
1363
- respan experiments get <id> # Get experiment details
1364
- respan experiments create # Create an experiment
1365
- respan eval <file> # Run a one-shot eval defined in a JSON file
1366
- \`\`\`
1367
-
1368
- ## One-Shot Evals via JSON
1369
-
1370
- \`respan eval <file.json>\` runs a complete eval pipeline from a single JSON spec —
1371
- creates the prompt, dataset, evaluators, and experiment in one command.
1372
- IDs are written back into the file so reruns reuse existing resources.
1373
-
1374
- ### Minimal example
1375
-
1376
- \`\`\`json
1377
- {
1378
- "name": "Movie matcher",
1379
- "prompt": {
1380
- "name": "movie-matcher",
1381
- "model": "gpt-5-mini",
1382
- "messages": [
1383
- { "role": "system", "content": "Identify the movie. Reply with only the title." },
1384
- { "role": "user", "content": "{{description}}" }
1385
- ]
1386
- },
1387
- "dataset": {
1388
- "name": "movie-descriptions",
1389
- "rows": [
1390
- { "description": "A detective investigates seven deadly sins.", "expected_output": "Se7en" },
1391
- { "description": "A hacker learns reality is a simulation.", "expected_output": "The Matrix" }
1392
- ]
1393
- },
1394
- "experiment": {
1395
- "evaluators": [
1396
- {
1397
- "name": "Title match",
1398
- "type": "llm",
1399
- "score_value_type": "boolean",
1400
- "llm_config": {
1401
- "model": "openai/gpt-5-mini",
1402
- "evaluator_definition": "Expected: {{expected_output}}. Got: {{output}}. Correct? Reply true or false."
1403
- }
1404
- }
1405
- ]
1406
- }
1407
- }
1408
- \`\`\`
1409
-
1410
- Run:
1411
-
1412
- \`\`\`bash
1413
- respan eval movie-matcher.eval.json
1414
- \`\`\`
1415
-
1416
- The runner creates the prompt (and deploys version 1), creates the dataset (and bulk-loads rows),
1417
- creates each evaluator + its workflow (and deploys), then creates the experiment linking them.
1418
- Each step writes the resulting IDs back to the JSON file so subsequent runs are idempotent —
1419
- re-running with the same file reuses the existing resources.
1420
- `;
1421
- export const MONITORS_MD = `# Monitors & Automation Reference
1422
-
1423
- Set up monitoring alerts, online evaluations, and automated workflows.
1424
-
1425
- Docs: \`https://respan.ai/docs/documentation/features/monitoring/monitors.md\`
1426
-
1427
- ---
1428
-
1429
- ## Monitors
1430
-
1431
- A monitor watches a metric over time and sends a notification when a threshold is crossed.
1432
-
1433
- ### Examples
1434
-
1435
- - Error rate > 5% over 10 minutes
1436
- - Total cost > $1.00 over 5 minutes
1437
- - Average latency > 3 seconds over 15 minutes
1438
- - P95 latency > 5 seconds over 1 hour
1439
-
1440
- ### Setup
1441
-
1442
- Monitors are configured in the platform UI: **Monitoring -> Monitors -> Create**.
1443
-
1444
- A monitor has two parts:
1445
-
1446
- #### 1. Trigger
1447
-
1448
- Template: \`When [metric] of [source] is [threshold] over [time window]\`
1449
-
1450
- - **Metric:** cost, latency, error rate, token usage, request count, etc.
1451
- - **Source:** all requests, specific model, specific customer, etc.
1452
- - **Threshold:** condition (greater than, less than, equals)
1453
- - **Time window:** evaluation period (1 min, 5 min, 10 min, 1 hour, etc.)
1454
-
1455
- Add **Where** conditions to narrow scope:
1456
- - Filter by model, project, environment, customer, or custom metadata
1457
-
1458
- #### 2. Notifications
1459
-
1460
- Destinations:
1461
-
1462
- | Destination | Details |
1463
- |-------------|---------|
1464
- | **Email** | Customizable subject and body |
1465
- | **Slack** | Customizable message |
1466
- | **Webhook** | Raw URL — receives JSON payload |
1467
-
1468
- A single monitor can send to multiple destinations.
1469
-
1470
- ### Manage
1471
-
1472
- - **Deploy** to make a monitor live
1473
- - **Pause/Resume** deployed monitors
1474
- - **Send test alert** to validate before deploying
1475
- - **View versions** and restore earlier versions as drafts
1476
- - **Create from chart** — select a metric on the dashboard, click to create a monitor with metric/source/interval prefilled
1477
-
1478
- ---
1479
-
1480
- ## Online Evaluations (Automation)
1481
-
1482
- Automatically score production traffic in real time using deployed evaluators.
1483
-
1484
- ### Setup
1485
-
1486
- Configure in the platform UI: **Evaluators -> Automations -> Create**.
1487
-
1488
- 1. **Create a condition** — filter rules for which logs to evaluate:
1489
- - **Single log** — evaluate individual spans matching criteria
1490
- - **Aggregation** — evaluate aggregated metrics over a window
1491
-
1492
- 2. **Create an automation** — connect:
1493
- - Condition (which logs)
1494
- - Evaluator (which scoring workflow)
1495
- - Sampling rate (0.0-1.0, fraction of matching logs to evaluate)
1496
-
1497
- 3. **Check results** — view eval scores in the automation table
1498
-
1499
- ### Use case
1500
-
1501
- > "Score 10% of production chat responses for hallucination using my LLM evaluator"
1502
-
1503
- \`\`\`
1504
- Condition: log_type = "chat" AND model = "gpt-4o"
1505
- Evaluator: hallucination-check
1506
- Sampling: 0.1
1507
- \`\`\`
1508
-
1509
- ---
1510
-
1511
- ## Webhooks
1512
-
1513
- Receive event notifications via HTTP webhooks.
1514
-
1515
- ### Setup
1516
-
1517
- 1. Go to platform: **Settings -> Webhooks -> Create**
1518
- 2. Enter your endpoint URL
1519
- 3. Select event types to subscribe to
1520
- 4. Optionally set a webhook secret for HMAC-SHA256 verification
1521
-
1522
- ### Verification
1523
-
1524
- Respan signs webhook payloads with HMAC-SHA256. Verify the signature:
1525
-
1526
- \`\`\`python
1527
- import hashlib
1528
- import hmac
1529
-
1530
- def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
1531
- expected = hmac.new(
1532
- secret.encode(),
1533
- payload,
1534
- hashlib.sha256,
1535
- ).hexdigest()
1536
- return hmac.compare_digest(expected, signature)
1537
- \`\`\`
1538
-
1539
- The signature is sent in the request headers.
1540
-
1541
- ---
1542
-
1543
- ## Metrics Dashboard
1544
-
1545
- Track key indicators in the platform: **Dashboard -> Metrics**.
1546
-
1547
- ### Available metrics
1548
-
1549
- - **Requests:** total count, requests per model/customer/API key
1550
- - **Tokens:** prompt tokens, completion tokens, total tokens
1551
- - **Cost:** per-request, per-model, per-customer
1552
- - **Latency:** average, P50, P95, P99
1553
- - **Errors:** error count, error rate
1554
- - **Time to first token (TTFT):** streaming latency
1555
-
1556
- ### Breakdowns
1557
-
1558
- Slice metrics by:
1559
- - Model
1560
- - Customer (\`customer_identifier\`)
1561
- - API key
1562
- - Prompt template
1563
- - Environment
1564
- - Custom metadata keys
1565
-
1566
- ### Per-user analytics
1567
-
1568
- Use \`customer_identifier\` in spans to enable:
1569
- - Per-user cost tracking and budgets
1570
- - Per-user rate limiting
1571
- - User-level quality metrics
1572
-
1573
- ---
1574
-
1575
- ## CLI Commands
1576
-
1577
- \`\`\`bash
1578
- respan traces list --limit 10 # List recent traces
1579
- respan traces get <trace-id> # Get trace details
1580
- respan traces summary # Trace summary stats
1581
- respan logs list --limit 10 # List recent logs
1582
- respan logs get <log-id> # Get log details
1583
- respan logs summary # Log summary stats
1584
- \`\`\`
1585
- `;