feat: documentation pages and updated model pricing
- Add 13 documentation pages under /docs (getting-started, concepts, SDK refs, integrations, API reference, self-hosting, OpenCode plugin) - Shared docs layout with collapsible sidebar navigation - Update model pricing across all SDKs: add GPT-5.x, GPT-4.1, o3/o4-mini, Claude 4.5 series, claude-opus-4-6 - Update trace-analytics context window lookup with current models
This commit is contained in:
629
apps/web/src/app/docs/api-reference/page.tsx
Normal file
629
apps/web/src/app/docs/api-reference/page.tsx
Normal file
@@ -0,0 +1,629 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "REST API Reference",
|
||||
description:
|
||||
"Complete API contract for AgentLens trace ingestion and retrieval endpoints.",
|
||||
};
|
||||
|
||||
function CodeBlock({ children, title }: { children: string; title?: string }) {
|
||||
return (
|
||||
<div className="rounded-xl overflow-hidden border border-neutral-800 bg-neutral-900/50 my-4">
|
||||
{title && (
|
||||
<div className="px-4 py-2.5 border-b border-neutral-800 text-xs text-neutral-500 font-mono">
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
<pre className="p-4 overflow-x-auto text-sm leading-relaxed">
|
||||
<code className="text-neutral-300">{children}</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function EndpointHeader({
|
||||
method,
|
||||
path,
|
||||
description,
|
||||
}: {
|
||||
method: string;
|
||||
path: string;
|
||||
description: string;
|
||||
}) {
|
||||
const methodColor =
|
||||
method === "POST"
|
||||
? "bg-amber-500/10 text-amber-400 border-amber-500/20"
|
||||
: "bg-emerald-500/10 text-emerald-400 border-emerald-500/20";
|
||||
return (
|
||||
<div className="mb-6">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span
|
||||
className={`px-2.5 py-1 rounded text-xs font-mono font-bold border ${methodColor}`}
|
||||
>
|
||||
{method}
|
||||
</span>
|
||||
<code className="text-lg font-mono text-neutral-200">{path}</code>
|
||||
</div>
|
||||
<p className="text-neutral-400">{description}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ApiReferencePage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold tracking-tight mb-4">
|
||||
REST API Reference
|
||||
</h1>
|
||||
<p className="text-lg text-neutral-400 mb-4 leading-relaxed">
|
||||
The AgentLens REST API is used by the SDKs to ingest and retrieve
|
||||
traces. You can also call it directly for custom integrations.
|
||||
</p>
|
||||
<div className="px-4 py-3 rounded-lg bg-neutral-900/50 border border-neutral-800/50 mb-10">
|
||||
<span className="text-sm text-neutral-400">Base URL: </span>
|
||||
<code className="text-sm font-mono text-emerald-400">
|
||||
https://agentlens.vectry.tech
|
||||
</code>
|
||||
</div>
|
||||
|
||||
<section className="mb-6">
|
||||
<h2 className="text-2xl font-semibold mb-4">Authentication</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
All write endpoints require a Bearer token in the Authorization header:
|
||||
</p>
|
||||
<CodeBlock>{`Authorization: Bearer your-api-key`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<hr className="border-neutral-800/50 my-10" />
|
||||
|
||||
<section className="mb-12">
|
||||
<EndpointHeader
|
||||
method="POST"
|
||||
path="/api/traces"
|
||||
description="Batch ingest one or more traces with their spans, decision points, and events."
|
||||
/>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-3">
|
||||
Request body
|
||||
</h3>
|
||||
<CodeBlock title="request_body.json">{`{
|
||||
"traces": [
|
||||
{
|
||||
"id": "trace-uuid-v4",
|
||||
"name": "my-agent-run",
|
||||
"sessionId": "session-abc",
|
||||
"status": "COMPLETED",
|
||||
"tags": ["production", "v2"],
|
||||
"metadata": { "user_id": "u-123" },
|
||||
"totalCost": 0.045,
|
||||
"totalTokens": 1500,
|
||||
"totalDuration": 3200,
|
||||
"startedAt": "2026-01-15T10:00:00.000Z",
|
||||
"endedAt": "2026-01-15T10:00:03.200Z",
|
||||
"spans": [ ... ],
|
||||
"decisionPoints": [ ... ],
|
||||
"events": [ ... ]
|
||||
}
|
||||
]
|
||||
}`}</CodeBlock>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-3 mt-8">
|
||||
TracePayload
|
||||
</h3>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Field</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Type</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Required</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">id</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">UUID v4 unique identifier</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">name</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">Human-readable trace name</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">sessionId</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Group traces into a session</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">status</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">enum</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">RUNNING | COMPLETED | ERROR</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">tags</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string[]</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">Array of tag strings</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">metadata</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">object</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Arbitrary JSON metadata</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">totalCost</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">number</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Total cost in USD</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">totalTokens</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">number</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Total token count</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">totalDuration</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">number</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Total duration in milliseconds</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">startedAt</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">ISO 8601 datetime</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">endedAt</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">ISO 8601 datetime (null if RUNNING)</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">spans</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">SpanPayload[]</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">Array of spans (can be empty)</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">decisionPoints</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">DecisionPointPayload[]</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">Array of decision points (can be empty)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">events</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">EventPayload[]</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">Array of events (can be empty)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-3 mt-8">
|
||||
SpanPayload
|
||||
</h3>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Field</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Type</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Required</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">id</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">UUID v4</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">parentSpanId</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Parent span ID for nesting</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">name</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">Span name</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">type</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">enum</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">LLM_CALL | TOOL_CALL | MEMORY_OP | CHAIN | AGENT | CUSTOM</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">input</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">object</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">JSON input payload</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">output</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">object</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">JSON output payload</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">tokenCount</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">number</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Total tokens</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">costUsd</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">number</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Cost in USD</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">durationMs</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">number</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Duration in milliseconds</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">status</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">enum</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">RUNNING | COMPLETED | ERROR</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">statusMessage</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Error message or status description</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">startedAt</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">ISO 8601 datetime</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">endedAt</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">ISO 8601 datetime</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">metadata</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">object</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Arbitrary JSON metadata</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-3 mt-8">
|
||||
DecisionPointPayload
|
||||
</h3>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Field</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Type</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Required</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">id</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">UUID v4</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">type</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">enum</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">TOOL_SELECTION | ROUTING | RETRY | ESCALATION | MEMORY_RETRIEVAL | PLANNING | CUSTOM</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">reasoning</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Why this choice was made</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">chosen</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">object</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">JSON value representing the choice made</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">alternatives</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">object[]</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">Array of alternatives considered</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">contextSnapshot</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">object</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Context at decision time</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">durationMs</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">number</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Decision time in milliseconds</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">costUsd</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">number</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Cost of this decision</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">parentSpanId</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Span this decision belongs to</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">timestamp</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">ISO 8601 datetime</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-3 mt-8">
|
||||
EventPayload
|
||||
</h3>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Field</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Type</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Required</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">id</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">UUID v4</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">spanId</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Span this event is associated with</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">type</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">enum</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">ERROR | RETRY | FALLBACK | CONTEXT_OVERFLOW | USER_FEEDBACK | CUSTOM</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">name</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">Human-readable event name</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">metadata</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">object</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Arbitrary JSON metadata</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">timestamp</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">ISO 8601 datetime</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-3 mt-8">
|
||||
Responses
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="p-3 rounded-lg border border-neutral-800/50 bg-neutral-900/30 flex items-start gap-3">
|
||||
<span className="px-2 py-0.5 rounded text-xs font-mono font-bold bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 flex-shrink-0">
|
||||
200
|
||||
</span>
|
||||
<div>
|
||||
<p className="text-sm text-neutral-300">Success</p>
|
||||
<code className="text-xs font-mono text-neutral-500">{`{ "success": true, "count": 1 }`}</code>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3 rounded-lg border border-neutral-800/50 bg-neutral-900/30 flex items-start gap-3">
|
||||
<span className="px-2 py-0.5 rounded text-xs font-mono font-bold bg-amber-500/10 text-amber-400 border border-amber-500/20 flex-shrink-0">
|
||||
400
|
||||
</span>
|
||||
<div>
|
||||
<p className="text-sm text-neutral-300">Bad Request</p>
|
||||
<code className="text-xs font-mono text-neutral-500">{`{ "error": "Request body must contain a 'traces' array" }`}</code>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3 rounded-lg border border-neutral-800/50 bg-neutral-900/30 flex items-start gap-3">
|
||||
<span className="px-2 py-0.5 rounded text-xs font-mono font-bold bg-red-500/10 text-red-400 border border-red-500/20 flex-shrink-0">
|
||||
401
|
||||
</span>
|
||||
<div>
|
||||
<p className="text-sm text-neutral-300">Unauthorized</p>
|
||||
<code className="text-xs font-mono text-neutral-500">{`{ "error": "Missing or invalid Authorization header" }`}</code>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3 rounded-lg border border-neutral-800/50 bg-neutral-900/30 flex items-start gap-3">
|
||||
<span className="px-2 py-0.5 rounded text-xs font-mono font-bold bg-amber-500/10 text-amber-400 border border-amber-500/20 flex-shrink-0">
|
||||
409
|
||||
</span>
|
||||
<div>
|
||||
<p className="text-sm text-neutral-300">Conflict</p>
|
||||
<code className="text-xs font-mono text-neutral-500">{`{ "error": "Duplicate trace ID detected" }`}</code>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3 rounded-lg border border-neutral-800/50 bg-neutral-900/30 flex items-start gap-3">
|
||||
<span className="px-2 py-0.5 rounded text-xs font-mono font-bold bg-red-500/10 text-red-400 border border-red-500/20 flex-shrink-0">
|
||||
500
|
||||
</span>
|
||||
<div>
|
||||
<p className="text-sm text-neutral-300">Internal Server Error</p>
|
||||
<code className="text-xs font-mono text-neutral-500">{`{ "error": "Internal server error" }`}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-3 mt-8">
|
||||
cURL example
|
||||
</h3>
|
||||
<CodeBlock title="terminal">{`curl -X POST https://agentlens.vectry.tech/api/traces \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-H "Authorization: Bearer your-api-key" \\
|
||||
-d '{
|
||||
"traces": [{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "test-trace",
|
||||
"status": "COMPLETED",
|
||||
"tags": ["test"],
|
||||
"startedAt": "2026-01-15T10:00:00.000Z",
|
||||
"endedAt": "2026-01-15T10:00:01.000Z",
|
||||
"spans": [],
|
||||
"decisionPoints": [],
|
||||
"events": []
|
||||
}]
|
||||
}'`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<hr className="border-neutral-800/50 my-10" />
|
||||
|
||||
<section>
|
||||
<EndpointHeader
|
||||
method="GET"
|
||||
path="/api/traces"
|
||||
description="List traces with pagination, filtering, and sorting."
|
||||
/>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-3">
|
||||
Query parameters
|
||||
</h3>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Param</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Type</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Default</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">page</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">integer</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">1</td>
|
||||
<td className="py-2">Page number (1-based)</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">limit</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">integer</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">20</td>
|
||||
<td className="py-2">Results per page (1-100)</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">status</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">-</td>
|
||||
<td className="py-2">Filter by status: RUNNING, COMPLETED, ERROR</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">search</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">-</td>
|
||||
<td className="py-2">Case-insensitive search on trace name</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">sessionId</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">-</td>
|
||||
<td className="py-2">Filter by session ID</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">tags</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">-</td>
|
||||
<td className="py-2">Comma-separated tags (matches any)</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">sort</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">newest</td>
|
||||
<td className="py-2">newest, oldest, longest, shortest, costliest</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">dateFrom</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">-</td>
|
||||
<td className="py-2">ISO 8601 lower bound</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">dateTo</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">-</td>
|
||||
<td className="py-2">ISO 8601 upper bound</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-3 mt-8">
|
||||
Response shape
|
||||
</h3>
|
||||
<CodeBlock title="response.json">{`{
|
||||
"traces": [
|
||||
{
|
||||
"id": "...",
|
||||
"name": "my-agent",
|
||||
"status": "COMPLETED",
|
||||
"tags": ["production"],
|
||||
"startedAt": "2026-01-15T10:00:00.000Z",
|
||||
"endedAt": "2026-01-15T10:00:03.200Z",
|
||||
"totalCost": 0.045,
|
||||
"totalTokens": 1500,
|
||||
"totalDuration": 3200,
|
||||
"_count": {
|
||||
"decisionPoints": 3,
|
||||
"spans": 7,
|
||||
"events": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"total": 142,
|
||||
"page": 1,
|
||||
"limit": 20,
|
||||
"totalPages": 8
|
||||
}`}</CodeBlock>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
279
apps/web/src/app/docs/concepts/page.tsx
Normal file
279
apps/web/src/app/docs/concepts/page.tsx
Normal file
@@ -0,0 +1,279 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Core Concepts",
|
||||
description:
|
||||
"Understand the four core data types in AgentLens: Traces, Spans, Decision Points, and Events.",
|
||||
};
|
||||
|
||||
function CodeBlock({ children, title }: { children: string; title?: string }) {
|
||||
return (
|
||||
<div className="rounded-xl overflow-hidden border border-neutral-800 bg-neutral-900/50 my-4">
|
||||
{title && (
|
||||
<div className="px-4 py-2.5 border-b border-neutral-800 text-xs text-neutral-500 font-mono">
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
<pre className="p-4 overflow-x-auto text-sm leading-relaxed">
|
||||
<code className="text-neutral-300">{children}</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ConceptCard({
|
||||
title,
|
||||
description,
|
||||
children,
|
||||
}: {
|
||||
title: string;
|
||||
description: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="mb-12 pb-12 border-b border-neutral-800/50 last:border-0">
|
||||
<h2 className="text-2xl font-semibold mb-3">{title}</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-6">{description}</p>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ConceptsPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold tracking-tight mb-4">
|
||||
Core Concepts
|
||||
</h1>
|
||||
<p className="text-lg text-neutral-400 mb-10 leading-relaxed">
|
||||
AgentLens organizes observability data into four core types. Together
|
||||
they give you a complete picture of what your agents do and why.
|
||||
</p>
|
||||
|
||||
<ConceptCard
|
||||
title="Trace"
|
||||
description="A Trace is the top-level container for a single agent execution. It groups all the work that happens from the moment your agent starts until it finishes. Every span, decision point, and event belongs to exactly one trace."
|
||||
>
|
||||
<h3 className="text-base font-medium text-neutral-200 mb-3">
|
||||
Properties
|
||||
</h3>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">
|
||||
Field
|
||||
</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">
|
||||
Type
|
||||
</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">
|
||||
Description
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">id</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2">Unique identifier (UUID v4)</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">name</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2">Human-readable label for the trace</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">status</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">enum</td>
|
||||
<td className="py-2">RUNNING, COMPLETED, or ERROR</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">tags</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string[]</td>
|
||||
<td className="py-2">Freeform labels for filtering</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">sessionId</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string?</td>
|
||||
<td className="py-2">Groups traces from the same session</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">startedAt</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">ISO datetime</td>
|
||||
<td className="py-2">When the trace began</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">endedAt</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">ISO datetime?</td>
|
||||
<td className="py-2">When the trace finished (null if still running)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</ConceptCard>
|
||||
|
||||
<ConceptCard
|
||||
title="Span"
|
||||
description="A Span represents a unit of work within a trace. Spans form a tree: each span can have a parent, creating a hierarchy that shows how work is nested. For example, an AGENT span may contain several LLM_CALL and TOOL_CALL child spans."
|
||||
>
|
||||
<h3 className="text-base font-medium text-neutral-200 mb-3">
|
||||
Span Types
|
||||
</h3>
|
||||
<div className="grid sm:grid-cols-2 gap-3 mb-6">
|
||||
{[
|
||||
{ type: "LLM_CALL", desc: "A call to a language model (OpenAI, Anthropic, etc.)" },
|
||||
{ type: "TOOL_CALL", desc: "An invocation of an external tool or function" },
|
||||
{ type: "MEMORY_OP", desc: "A read or write to a vector store or memory system" },
|
||||
{ type: "CHAIN", desc: "A sequential pipeline of operations" },
|
||||
{ type: "AGENT", desc: "A top-level agent or sub-agent execution" },
|
||||
{ type: "CUSTOM", desc: "Any user-defined operation type" },
|
||||
].map((item) => (
|
||||
<div
|
||||
key={item.type}
|
||||
className="p-3 rounded-lg border border-neutral-800/50 bg-neutral-900/30"
|
||||
>
|
||||
<span className="font-mono text-xs text-emerald-400">
|
||||
{item.type}
|
||||
</span>
|
||||
<p className="text-xs text-neutral-500 mt-1">{item.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<h3 className="text-base font-medium text-neutral-200 mb-3">
|
||||
Nesting example
|
||||
</h3>
|
||||
<CodeBlock>{`Trace: "research-agent"
|
||||
Span: "agent" (AGENT)
|
||||
Span: "plan" (LLM_CALL)
|
||||
Span: "web-search" (TOOL_CALL)
|
||||
Span: "summarize" (LLM_CALL)`}</CodeBlock>
|
||||
|
||||
<h3 className="text-base font-medium text-neutral-200 mb-3 mt-6">
|
||||
Key properties
|
||||
</h3>
|
||||
<ul className="text-sm text-neutral-400 space-y-2 ml-1">
|
||||
<li>
|
||||
<span className="font-mono text-emerald-400 text-xs">input</span> / <span className="font-mono text-emerald-400 text-xs">output</span> — JSON payloads capturing what went in and came out
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-mono text-emerald-400 text-xs">tokenCount</span> — Total tokens consumed (for LLM_CALL spans)
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-mono text-emerald-400 text-xs">costUsd</span> — Dollar cost of this span
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-mono text-emerald-400 text-xs">durationMs</span> — Wall-clock time in milliseconds
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-mono text-emerald-400 text-xs">parentSpanId</span> — Reference to the parent span (null for root spans)
|
||||
</li>
|
||||
</ul>
|
||||
</ConceptCard>
|
||||
|
||||
<ConceptCard
|
||||
title="Decision Point"
|
||||
description="A Decision Point records where your agent chose between alternatives. This is what separates AgentLens from generic tracing tools: you see the reasoning, what was chosen, and what was considered but rejected."
|
||||
>
|
||||
<h3 className="text-base font-medium text-neutral-200 mb-3">
|
||||
Decision Point Types
|
||||
</h3>
|
||||
<div className="grid sm:grid-cols-2 gap-3 mb-6">
|
||||
{[
|
||||
{ type: "TOOL_SELECTION", desc: "Agent chose which tool to call" },
|
||||
{ type: "ROUTING", desc: "Agent routed to a specific sub-agent or branch" },
|
||||
{ type: "RETRY", desc: "Agent decided to retry a failed operation" },
|
||||
{ type: "ESCALATION", desc: "Agent escalated to a human or higher-level agent" },
|
||||
{ type: "MEMORY_RETRIEVAL", desc: "Agent chose what context to retrieve" },
|
||||
{ type: "PLANNING", desc: "Agent formulated a multi-step plan" },
|
||||
].map((item) => (
|
||||
<div
|
||||
key={item.type}
|
||||
className="p-3 rounded-lg border border-neutral-800/50 bg-neutral-900/30"
|
||||
>
|
||||
<span className="font-mono text-xs text-emerald-400">
|
||||
{item.type}
|
||||
</span>
|
||||
<p className="text-xs text-neutral-500 mt-1">{item.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<h3 className="text-base font-medium text-neutral-200 mb-3">
|
||||
Structure
|
||||
</h3>
|
||||
<CodeBlock title="decision_point.json">{`{
|
||||
"type": "TOOL_SELECTION",
|
||||
"reasoning": "User asked about weather, need real-time data",
|
||||
"chosen": { "tool": "weather_api", "confidence": 0.95 },
|
||||
"alternatives": [
|
||||
{ "tool": "web_search", "confidence": 0.72 },
|
||||
{ "tool": "knowledge_base", "confidence": 0.31 }
|
||||
],
|
||||
"contextSnapshot": { "user_intent": "weather_query" }
|
||||
}`}</CodeBlock>
|
||||
</ConceptCard>
|
||||
|
||||
<ConceptCard
|
||||
title="Event"
|
||||
description="An Event is a discrete occurrence during a trace that does not represent a unit of work but is worth recording. Events capture errors, retries, fallbacks, and other notable moments."
|
||||
>
|
||||
<h3 className="text-base font-medium text-neutral-200 mb-3">
|
||||
Event Types
|
||||
</h3>
|
||||
<div className="grid sm:grid-cols-2 gap-3 mb-6">
|
||||
{[
|
||||
{ type: "ERROR", desc: "An exception or failure occurred" },
|
||||
{ type: "RETRY", desc: "An operation was retried" },
|
||||
{ type: "FALLBACK", desc: "A fallback path was triggered" },
|
||||
{ type: "CONTEXT_OVERFLOW", desc: "Context window limit was exceeded" },
|
||||
{ type: "USER_FEEDBACK", desc: "User provided feedback on an output" },
|
||||
{ type: "CUSTOM", desc: "Any user-defined event type" },
|
||||
].map((item) => (
|
||||
<div
|
||||
key={item.type}
|
||||
className="p-3 rounded-lg border border-neutral-800/50 bg-neutral-900/30"
|
||||
>
|
||||
<span className="font-mono text-xs text-emerald-400">
|
||||
{item.type}
|
||||
</span>
|
||||
<p className="text-xs text-neutral-500 mt-1">{item.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<h3 className="text-base font-medium text-neutral-200 mb-3">
|
||||
Example
|
||||
</h3>
|
||||
<CodeBlock title="event.json">{`{
|
||||
"type": "CONTEXT_OVERFLOW",
|
||||
"name": "token-limit-exceeded",
|
||||
"metadata": {
|
||||
"limit": 128000,
|
||||
"actual": 131072,
|
||||
"truncated_chars": 4200
|
||||
},
|
||||
"timestamp": "2026-01-15T10:30:00.000Z"
|
||||
}`}</CodeBlock>
|
||||
</ConceptCard>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold mb-4">How they fit together</h2>
|
||||
<CodeBlock>{`Trace: "customer-support-agent"
|
||||
|
|
||||
+-- Span: "classify-intent" (LLM_CALL)
|
||||
| Decision: ROUTING -> chose "refund-flow" over "faq-flow"
|
||||
|
|
||||
+-- Span: "refund-flow" (AGENT)
|
||||
| +-- Span: "lookup-order" (TOOL_CALL)
|
||||
| +-- Span: "process-refund" (TOOL_CALL)
|
||||
| Event: ERROR -> "payment-gateway-timeout"
|
||||
| Event: RETRY -> "retrying with backup gateway"
|
||||
| +-- Span: "process-refund-retry" (TOOL_CALL)
|
||||
|
|
||||
+-- Span: "compose-response" (LLM_CALL)`}</CodeBlock>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
121
apps/web/src/app/docs/docs-sidebar.tsx
Normal file
121
apps/web/src/app/docs/docs-sidebar.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
interface NavItem {
|
||||
title: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
interface NavSection {
|
||||
heading: string;
|
||||
items: NavItem[];
|
||||
}
|
||||
|
||||
const navigation: NavSection[] = [
|
||||
{
|
||||
heading: "Overview",
|
||||
items: [
|
||||
{ title: "Introduction", href: "/docs" },
|
||||
{ title: "Getting Started", href: "/docs/getting-started" },
|
||||
{ title: "Core Concepts", href: "/docs/concepts" },
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "SDKs",
|
||||
items: [
|
||||
{ title: "Python SDK", href: "/docs/python-sdk" },
|
||||
{ title: "TypeScript SDK", href: "/docs/typescript-sdk" },
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Integrations",
|
||||
items: [
|
||||
{ title: "OpenAI", href: "/docs/integrations/openai" },
|
||||
{ title: "Anthropic", href: "/docs/integrations/anthropic" },
|
||||
{ title: "LangChain", href: "/docs/integrations/langchain" },
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Tools",
|
||||
items: [{ title: "OpenCode Plugin", href: "/docs/opencode-plugin" }],
|
||||
},
|
||||
{
|
||||
heading: "Reference",
|
||||
items: [
|
||||
{ title: "REST API", href: "/docs/api-reference" },
|
||||
{ title: "Self-Hosting", href: "/docs/self-hosting" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function SidebarContent() {
|
||||
return (
|
||||
<nav className="space-y-6">
|
||||
{navigation.map((section) => (
|
||||
<div key={section.heading}>
|
||||
<h4 className="text-xs font-semibold uppercase tracking-wider text-neutral-500 mb-2 px-3">
|
||||
{section.heading}
|
||||
</h4>
|
||||
<ul className="space-y-0.5">
|
||||
{section.items.map((item) => (
|
||||
<li key={item.href}>
|
||||
<a
|
||||
href={item.href}
|
||||
className="block px-3 py-1.5 text-sm text-neutral-400 hover:text-neutral-100 hover:bg-neutral-800/50 rounded-md transition-colors"
|
||||
>
|
||||
{item.title}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
export function DocsSidebar() {
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMobileOpen(!mobileOpen)}
|
||||
className="lg:hidden fixed bottom-4 right-4 z-50 w-12 h-12 rounded-full bg-emerald-500 text-neutral-950 shadow-lg shadow-emerald-500/25 flex items-center justify-center"
|
||||
aria-label="Toggle navigation"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
{mobileOpen ? (
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
) : (
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M4 6h16M4 12h16M4 18h16" />
|
||||
)}
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{mobileOpen && (
|
||||
<div className="lg:hidden fixed inset-0 z-40">
|
||||
<div
|
||||
className="absolute inset-0 bg-neutral-950/80 backdrop-blur-sm"
|
||||
onClick={() => setMobileOpen(false)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Escape") setMobileOpen(false);
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label="Close navigation"
|
||||
/>
|
||||
<div className="absolute left-0 top-14 bottom-0 w-72 bg-neutral-950 border-r border-neutral-800/50 p-6 overflow-y-auto">
|
||||
<SidebarContent />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<aside className="hidden lg:block w-64 flex-shrink-0 border-r border-neutral-800/50 sticky top-14 h-[calc(100vh-3.5rem)] overflow-y-auto py-8 px-4">
|
||||
<SidebarContent />
|
||||
</aside>
|
||||
</>
|
||||
);
|
||||
}
|
||||
226
apps/web/src/app/docs/getting-started/page.tsx
Normal file
226
apps/web/src/app/docs/getting-started/page.tsx
Normal file
@@ -0,0 +1,226 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Getting Started",
|
||||
description:
|
||||
"Install AgentLens, initialize the SDK, and send your first trace in under five minutes.",
|
||||
};
|
||||
|
||||
function CodeBlock({ children, title }: { children: string; title?: string }) {
|
||||
return (
|
||||
<div className="rounded-xl overflow-hidden border border-neutral-800 bg-neutral-900/50 my-4">
|
||||
{title && (
|
||||
<div className="px-4 py-2.5 border-b border-neutral-800 text-xs text-neutral-500 font-mono">
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
<pre className="p-4 overflow-x-auto text-sm leading-relaxed">
|
||||
<code className="text-neutral-300">{children}</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function GettingStartedPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold tracking-tight mb-4">
|
||||
Getting Started
|
||||
</h1>
|
||||
<p className="text-lg text-neutral-400 mb-10 leading-relaxed">
|
||||
Go from zero to full agent observability in under five minutes. This
|
||||
guide walks you through installing the SDK, initializing it, and sending
|
||||
your first trace.
|
||||
</p>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Prerequisites</h2>
|
||||
<ul className="list-disc list-inside text-neutral-400 space-y-2 ml-1">
|
||||
<li>Python 3.9+ or Node.js 18+</li>
|
||||
<li>
|
||||
An AgentLens instance (use{" "}
|
||||
<a
|
||||
href="https://agentlens.vectry.tech"
|
||||
className="text-emerald-400 hover:underline"
|
||||
>
|
||||
agentlens.vectry.tech
|
||||
</a>{" "}
|
||||
or{" "}
|
||||
<a
|
||||
href="/docs/self-hosting"
|
||||
className="text-emerald-400 hover:underline"
|
||||
>
|
||||
self-host
|
||||
</a>
|
||||
)
|
||||
</li>
|
||||
<li>An API key for authentication</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">
|
||||
Step 1: Install the SDK
|
||||
</h2>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-2">Python</h3>
|
||||
<CodeBlock title="terminal">pip install vectry-agentlens</CodeBlock>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-2 mt-6">
|
||||
TypeScript / Node.js
|
||||
</h3>
|
||||
<CodeBlock title="terminal">npm install agentlens-sdk</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">
|
||||
Step 2: Initialize AgentLens
|
||||
</h2>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-2">Python</h3>
|
||||
<CodeBlock title="main.py">{`import agentlens
|
||||
|
||||
agentlens.init(
|
||||
api_key="your-api-key",
|
||||
endpoint="https://agentlens.vectry.tech"
|
||||
)`}</CodeBlock>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-2 mt-6">
|
||||
TypeScript
|
||||
</h3>
|
||||
<CodeBlock title="index.ts">{`import { init } from "agentlens-sdk";
|
||||
|
||||
init({
|
||||
apiKey: "your-api-key",
|
||||
endpoint: "https://agentlens.vectry.tech",
|
||||
});`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">
|
||||
Step 3: Trace your first agent
|
||||
</h2>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-2">Python</h3>
|
||||
<CodeBlock title="agent.py">{`import agentlens
|
||||
from agentlens import trace
|
||||
|
||||
agentlens.init(
|
||||
api_key="your-api-key",
|
||||
endpoint="https://agentlens.vectry.tech"
|
||||
)
|
||||
|
||||
@trace(name="my-first-agent")
|
||||
def my_agent(prompt: str) -> str:
|
||||
# Your agent logic here
|
||||
response = call_llm(prompt)
|
||||
return response
|
||||
|
||||
# Run it — the trace is sent automatically
|
||||
result = my_agent("What is the capital of France?")`}</CodeBlock>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-2 mt-6">
|
||||
TypeScript
|
||||
</h3>
|
||||
<CodeBlock title="agent.ts">{`import { init, TraceBuilder } from "agentlens-sdk";
|
||||
|
||||
init({
|
||||
apiKey: "your-api-key",
|
||||
endpoint: "https://agentlens.vectry.tech",
|
||||
});
|
||||
|
||||
const trace = new TraceBuilder("my-first-agent");
|
||||
|
||||
trace.addSpan({
|
||||
name: "llm-call",
|
||||
type: "LLM_CALL",
|
||||
input: { prompt: "What is the capital of France?" },
|
||||
output: { response: "Paris" },
|
||||
status: "COMPLETED",
|
||||
});
|
||||
|
||||
await trace.end();`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">
|
||||
Step 4: View in the dashboard
|
||||
</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
Open your AgentLens dashboard to see the trace you just sent. You will
|
||||
see the trace name, its status, timing information, and any spans or
|
||||
decision points you recorded.
|
||||
</p>
|
||||
<a
|
||||
href="/dashboard"
|
||||
className="inline-flex items-center gap-2 px-5 py-2.5 bg-emerald-500 hover:bg-emerald-400 text-neutral-950 font-semibold rounded-lg transition-colors text-sm"
|
||||
>
|
||||
Open Dashboard
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M13 7l5 5m0 0l-5 5m5-5H6"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Next steps</h2>
|
||||
<div className="grid sm:grid-cols-2 gap-4">
|
||||
<a
|
||||
href="/docs/concepts"
|
||||
className="group block p-4 rounded-xl border border-neutral-800/50 hover:border-emerald-500/30 transition-colors"
|
||||
>
|
||||
<h3 className="text-sm font-semibold text-neutral-200 group-hover:text-emerald-400 transition-colors">
|
||||
Core Concepts
|
||||
</h3>
|
||||
<p className="text-xs text-neutral-500 mt-1">
|
||||
Learn about Traces, Spans, Decision Points, and Events.
|
||||
</p>
|
||||
</a>
|
||||
<a
|
||||
href="/docs/python-sdk"
|
||||
className="group block p-4 rounded-xl border border-neutral-800/50 hover:border-emerald-500/30 transition-colors"
|
||||
>
|
||||
<h3 className="text-sm font-semibold text-neutral-200 group-hover:text-emerald-400 transition-colors">
|
||||
Python SDK Reference
|
||||
</h3>
|
||||
<p className="text-xs text-neutral-500 mt-1">
|
||||
Explore the full Python SDK API surface.
|
||||
</p>
|
||||
</a>
|
||||
<a
|
||||
href="/docs/integrations/openai"
|
||||
className="group block p-4 rounded-xl border border-neutral-800/50 hover:border-emerald-500/30 transition-colors"
|
||||
>
|
||||
<h3 className="text-sm font-semibold text-neutral-200 group-hover:text-emerald-400 transition-colors">
|
||||
OpenAI Integration
|
||||
</h3>
|
||||
<p className="text-xs text-neutral-500 mt-1">
|
||||
Auto-trace OpenAI calls with a single wrapper.
|
||||
</p>
|
||||
</a>
|
||||
<a
|
||||
href="/docs/self-hosting"
|
||||
className="group block p-4 rounded-xl border border-neutral-800/50 hover:border-emerald-500/30 transition-colors"
|
||||
>
|
||||
<h3 className="text-sm font-semibold text-neutral-200 group-hover:text-emerald-400 transition-colors">
|
||||
Self-Hosting
|
||||
</h3>
|
||||
<p className="text-xs text-neutral-500 mt-1">
|
||||
Deploy your own AgentLens instance with Docker.
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
191
apps/web/src/app/docs/integrations/anthropic/page.tsx
Normal file
191
apps/web/src/app/docs/integrations/anthropic/page.tsx
Normal file
@@ -0,0 +1,191 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Anthropic Integration",
|
||||
description:
|
||||
"Wrap the Anthropic client to automatically trace Claude API calls with full metadata capture.",
|
||||
};
|
||||
|
||||
function CodeBlock({ children, title }: { children: string; title?: string }) {
|
||||
return (
|
||||
<div className="rounded-xl overflow-hidden border border-neutral-800 bg-neutral-900/50 my-4">
|
||||
{title && (
|
||||
<div className="px-4 py-2.5 border-b border-neutral-800 text-xs text-neutral-500 font-mono">
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
<pre className="p-4 overflow-x-auto text-sm leading-relaxed">
|
||||
<code className="text-neutral-300">{children}</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AnthropicIntegrationPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold tracking-tight mb-4">
|
||||
Anthropic Integration
|
||||
</h1>
|
||||
<p className="text-lg text-neutral-400 mb-10 leading-relaxed">
|
||||
Wrap the Anthropic Python client to automatically trace all Claude API
|
||||
calls. AgentLens captures model, token usage, cost, latency, and the
|
||||
full message exchange.
|
||||
</p>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Installation</h2>
|
||||
<CodeBlock title="terminal">{`pip install vectry-agentlens anthropic`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Quick setup</h2>
|
||||
<CodeBlock title="main.py">{`import agentlens
|
||||
from agentlens.integrations.anthropic import wrap_anthropic
|
||||
import anthropic
|
||||
|
||||
agentlens.init(
|
||||
api_key="your-api-key",
|
||||
endpoint="https://agentlens.vectry.tech",
|
||||
)
|
||||
|
||||
client = wrap_anthropic(anthropic.Anthropic())
|
||||
|
||||
response = client.messages.create(
|
||||
model="claude-sonnet-4-20250514",
|
||||
max_tokens=1024,
|
||||
messages=[
|
||||
{"role": "user", "content": "Explain the halting problem."},
|
||||
],
|
||||
)`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">What gets captured</h2>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Field</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">input.model</td>
|
||||
<td className="py-2">Model name (claude-sonnet-4-20250514, claude-haiku, etc.)</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">input.messages</td>
|
||||
<td className="py-2">Full message array sent to the API</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">input.system</td>
|
||||
<td className="py-2">System prompt if provided</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">output.content</td>
|
||||
<td className="py-2">Response content blocks</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">tokenCount</td>
|
||||
<td className="py-2">Input tokens + output tokens</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">costUsd</td>
|
||||
<td className="py-2">Estimated cost based on model pricing</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">durationMs</td>
|
||||
<td className="py-2">Wall-clock request time</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">metadata.stop_reason</td>
|
||||
<td className="py-2">How generation ended (end_turn, max_tokens, tool_use)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Async client</h2>
|
||||
<CodeBlock title="async_example.py">{`from agentlens.integrations.anthropic import wrap_anthropic
|
||||
import anthropic
|
||||
|
||||
async_client = wrap_anthropic(anthropic.AsyncAnthropic())
|
||||
|
||||
response = await async_client.messages.create(
|
||||
model="claude-sonnet-4-20250514",
|
||||
max_tokens=1024,
|
||||
messages=[{"role": "user", "content": "Hello!"}],
|
||||
)`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">
|
||||
Combining with @trace
|
||||
</h2>
|
||||
<CodeBlock title="combined.py">{`import agentlens
|
||||
from agentlens import trace
|
||||
from agentlens.integrations.anthropic import wrap_anthropic
|
||||
import anthropic
|
||||
|
||||
agentlens.init(api_key="...", endpoint="...")
|
||||
client = wrap_anthropic(anthropic.Anthropic())
|
||||
|
||||
@trace(name="analysis-agent")
|
||||
async def analyze(document: str) -> str:
|
||||
response = client.messages.create(
|
||||
model="claude-sonnet-4-20250514",
|
||||
max_tokens=2048,
|
||||
system="You are a document analysis expert.",
|
||||
messages=[{"role": "user", "content": f"Analyze: {document}"}],
|
||||
)
|
||||
return response.content[0].text`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Tool use</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
When Claude invokes tools, AgentLens captures each tool use as a
|
||||
TOOL_SELECTION decision point automatically:
|
||||
</p>
|
||||
<CodeBlock title="tools.py">{`@trace(name="claude-tool-agent")
|
||||
async def tool_agent(prompt: str):
|
||||
response = client.messages.create(
|
||||
model="claude-sonnet-4-20250514",
|
||||
max_tokens=1024,
|
||||
tools=[{
|
||||
"name": "get_stock_price",
|
||||
"description": "Get the current stock price for a ticker symbol",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ticker": {
|
||||
"type": "string",
|
||||
"description": "Stock ticker symbol"
|
||||
}
|
||||
},
|
||||
"required": ["ticker"]
|
||||
}
|
||||
}],
|
||||
messages=[{"role": "user", "content": prompt}],
|
||||
)
|
||||
return response`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold mb-4">Supported API methods</h2>
|
||||
<ul className="text-sm text-neutral-400 space-y-2 ml-1">
|
||||
<li>
|
||||
<code className="font-mono text-emerald-400 text-xs">messages.create()</code> — Message creation (including streaming)
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono text-emerald-400 text-xs">messages.count_tokens()</code> — Token counting
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
213
apps/web/src/app/docs/integrations/langchain/page.tsx
Normal file
213
apps/web/src/app/docs/integrations/langchain/page.tsx
Normal file
@@ -0,0 +1,213 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "LangChain Integration",
|
||||
description:
|
||||
"Use the AgentLensCallbackHandler to trace LangChain chains, agents, and tool invocations.",
|
||||
};
|
||||
|
||||
function CodeBlock({ children, title }: { children: string; title?: string }) {
|
||||
return (
|
||||
<div className="rounded-xl overflow-hidden border border-neutral-800 bg-neutral-900/50 my-4">
|
||||
{title && (
|
||||
<div className="px-4 py-2.5 border-b border-neutral-800 text-xs text-neutral-500 font-mono">
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
<pre className="p-4 overflow-x-auto text-sm leading-relaxed">
|
||||
<code className="text-neutral-300">{children}</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function LangChainIntegrationPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold tracking-tight mb-4">
|
||||
LangChain Integration
|
||||
</h1>
|
||||
<p className="text-lg text-neutral-400 mb-10 leading-relaxed">
|
||||
The AgentLensCallbackHandler plugs into LangChain's callback system
|
||||
to automatically trace chains, agents, LLM calls, and tool invocations
|
||||
without changing your existing code.
|
||||
</p>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Installation</h2>
|
||||
<CodeBlock title="terminal">{`pip install vectry-agentlens langchain langchain-openai`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Quick setup</h2>
|
||||
<CodeBlock title="main.py">{`import agentlens
|
||||
from agentlens.integrations.langchain import AgentLensCallbackHandler
|
||||
|
||||
agentlens.init(
|
||||
api_key="your-api-key",
|
||||
endpoint="https://agentlens.vectry.tech",
|
||||
)
|
||||
|
||||
handler = AgentLensCallbackHandler()`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Using with chains</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
Pass the handler in the <code className="text-emerald-400 font-mono text-xs bg-emerald-500/5 px-1.5 py-0.5 rounded">callbacks</code> config:
|
||||
</p>
|
||||
<CodeBlock title="chain_example.py">{`from langchain_openai import ChatOpenAI
|
||||
from langchain_core.prompts import ChatPromptTemplate
|
||||
from langchain_core.output_parsers import StrOutputParser
|
||||
|
||||
llm = ChatOpenAI(model="gpt-4o")
|
||||
prompt = ChatPromptTemplate.from_messages([
|
||||
("system", "You are a helpful assistant."),
|
||||
("user", "{input}"),
|
||||
])
|
||||
|
||||
chain = prompt | llm | StrOutputParser()
|
||||
|
||||
result = chain.invoke(
|
||||
{"input": "Explain recursion"},
|
||||
config={"callbacks": [handler]},
|
||||
)`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Using with agents</h2>
|
||||
<CodeBlock title="agent_example.py">{`from langchain_openai import ChatOpenAI
|
||||
from langchain.agents import AgentExecutor, create_tool_calling_agent
|
||||
from langchain_core.prompts import ChatPromptTemplate
|
||||
from langchain_core.tools import tool
|
||||
|
||||
@tool
|
||||
def calculator(expression: str) -> str:
|
||||
"""Evaluate a math expression."""
|
||||
return str(eval(expression))
|
||||
|
||||
llm = ChatOpenAI(model="gpt-4o")
|
||||
prompt = ChatPromptTemplate.from_messages([
|
||||
("system", "You are a helpful math assistant."),
|
||||
("user", "{input}"),
|
||||
("placeholder", "{agent_scratchpad}"),
|
||||
])
|
||||
|
||||
agent = create_tool_calling_agent(llm, [calculator], prompt)
|
||||
executor = AgentExecutor(agent=agent, tools=[calculator])
|
||||
|
||||
result = executor.invoke(
|
||||
{"input": "What is 42 * 17 + 3?"},
|
||||
config={"callbacks": [handler]},
|
||||
)`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">What gets captured</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
The callback handler maps LangChain events to AgentLens concepts:
|
||||
</p>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">LangChain Event</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">AgentLens Type</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Captured Data</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 text-neutral-400">Chain start/end</td>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">CHAIN span</td>
|
||||
<td className="py-2">Input/output, duration</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 text-neutral-400">LLM start/end</td>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">LLM_CALL span</td>
|
||||
<td className="py-2">Model, messages, tokens, cost, duration</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 text-neutral-400">Tool start/end</td>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">TOOL_CALL span</td>
|
||||
<td className="py-2">Tool name, input args, output, duration</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 text-neutral-400">Agent action</td>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">TOOL_SELECTION decision</td>
|
||||
<td className="py-2">Selected tool, reasoning</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 text-neutral-400">Retry</td>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">RETRY event</td>
|
||||
<td className="py-2">Error message, attempt count</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 text-neutral-400">Error</td>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">ERROR event</td>
|
||||
<td className="py-2">Exception type, message, traceback</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Global callbacks</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
To trace all LangChain operations without passing callbacks
|
||||
individually, set the handler globally:
|
||||
</p>
|
||||
<CodeBlock title="global.py">{`from langchain_core.globals import set_llm_cache
|
||||
from langchain.callbacks.manager import set_handler
|
||||
|
||||
set_handler(handler)
|
||||
|
||||
# Now all chains and agents are traced automatically
|
||||
result = chain.invoke({"input": "Hello"})
|
||||
# No need to pass config={"callbacks": [handler]}`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold mb-4">Handler options</h2>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Parameter</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Type</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Default</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">trace_name</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">str | None</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">None</td>
|
||||
<td className="py-2">Override the default trace name</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">tags</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">list[str]</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">[]</td>
|
||||
<td className="py-2">Tags to attach to all traces</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">capture_io</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">bool</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">True</td>
|
||||
<td className="py-2">Capture input/output payloads</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<CodeBlock title="options.py">{`handler = AgentLensCallbackHandler(
|
||||
trace_name="my-langchain-app",
|
||||
tags=["production", "langchain"],
|
||||
capture_io=True,
|
||||
)`}</CodeBlock>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
202
apps/web/src/app/docs/integrations/openai/page.tsx
Normal file
202
apps/web/src/app/docs/integrations/openai/page.tsx
Normal file
@@ -0,0 +1,202 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "OpenAI Integration",
|
||||
description:
|
||||
"Auto-trace all OpenAI API calls with a single wrapper. Captures model, tokens, cost, and latency.",
|
||||
};
|
||||
|
||||
function CodeBlock({ children, title }: { children: string; title?: string }) {
|
||||
return (
|
||||
<div className="rounded-xl overflow-hidden border border-neutral-800 bg-neutral-900/50 my-4">
|
||||
{title && (
|
||||
<div className="px-4 py-2.5 border-b border-neutral-800 text-xs text-neutral-500 font-mono">
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
<pre className="p-4 overflow-x-auto text-sm leading-relaxed">
|
||||
<code className="text-neutral-300">{children}</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function OpenAIIntegrationPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold tracking-tight mb-4">
|
||||
OpenAI Integration
|
||||
</h1>
|
||||
<p className="text-lg text-neutral-400 mb-10 leading-relaxed">
|
||||
Wrap the OpenAI client once and every API call is automatically traced.
|
||||
AgentLens captures the model name, token usage, cost, latency, input
|
||||
messages, and output completions.
|
||||
</p>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Installation</h2>
|
||||
<CodeBlock title="terminal">{`pip install vectry-agentlens openai`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Quick setup</h2>
|
||||
<CodeBlock title="main.py">{`import agentlens
|
||||
from agentlens.integrations.openai import wrap_openai
|
||||
import openai
|
||||
|
||||
agentlens.init(
|
||||
api_key="your-api-key",
|
||||
endpoint="https://agentlens.vectry.tech",
|
||||
)
|
||||
|
||||
client = wrap_openai(openai.OpenAI())
|
||||
|
||||
# All calls are now auto-traced
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "Explain quantum computing in one paragraph."},
|
||||
],
|
||||
)`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">What gets captured</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
Each OpenAI API call creates an LLM_CALL span with the following data:
|
||||
</p>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Field</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">input.model</td>
|
||||
<td className="py-2">Model name (gpt-4o, gpt-4o-mini, etc.)</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">input.messages</td>
|
||||
<td className="py-2">Full message array sent to the API</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">output.content</td>
|
||||
<td className="py-2">Response content from the model</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">tokenCount</td>
|
||||
<td className="py-2">Total tokens (prompt + completion)</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">costUsd</td>
|
||||
<td className="py-2">Estimated cost based on model pricing</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">durationMs</td>
|
||||
<td className="py-2">Wall-clock time for the request</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">metadata.finish_reason</td>
|
||||
<td className="py-2">How the model stopped (stop, length, tool_calls)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Async client</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
The wrapper works with both sync and async OpenAI clients:
|
||||
</p>
|
||||
<CodeBlock title="async_example.py">{`from agentlens.integrations.openai import wrap_openai
|
||||
import openai
|
||||
|
||||
async_client = wrap_openai(openai.AsyncOpenAI())
|
||||
|
||||
response = await async_client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Hello!"}],
|
||||
)`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">
|
||||
Combining with @trace
|
||||
</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
When used inside a <code className="text-emerald-400 font-mono text-xs bg-emerald-500/5 px-1.5 py-0.5 rounded">@trace</code>-decorated
|
||||
function, OpenAI calls appear as child spans of the trace:
|
||||
</p>
|
||||
<CodeBlock title="combined.py">{`import agentlens
|
||||
from agentlens import trace
|
||||
from agentlens.integrations.openai import wrap_openai
|
||||
import openai
|
||||
|
||||
agentlens.init(api_key="...", endpoint="...")
|
||||
client = wrap_openai(openai.OpenAI())
|
||||
|
||||
@trace(name="research-agent")
|
||||
async def research(topic: str) -> str:
|
||||
# This LLM call becomes a child span of "research-agent"
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[
|
||||
{"role": "system", "content": "Summarize the following topic."},
|
||||
{"role": "user", "content": topic},
|
||||
],
|
||||
)
|
||||
return response.choices[0].message.content`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Tool calls</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
When the model invokes tools (function calling), AgentLens
|
||||
automatically captures each tool call as a TOOL_SELECTION decision
|
||||
point and the tool execution as a TOOL_CALL span:
|
||||
</p>
|
||||
<CodeBlock title="tools.py">{`@trace(name="tool-agent")
|
||||
async def agent_with_tools(prompt: str):
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": prompt}],
|
||||
tools=[{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get weather for a city",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"city": {"type": "string"}
|
||||
},
|
||||
},
|
||||
},
|
||||
}],
|
||||
)
|
||||
# AgentLens captures the tool selection decision automatically
|
||||
return response`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold mb-4">Supported API methods</h2>
|
||||
<ul className="text-sm text-neutral-400 space-y-2 ml-1">
|
||||
<li>
|
||||
<code className="font-mono text-emerald-400 text-xs">chat.completions.create()</code> — Chat completions (including streaming)
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono text-emerald-400 text-xs">completions.create()</code> — Legacy completions
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono text-emerald-400 text-xs">embeddings.create()</code> — Embedding generation
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
63
apps/web/src/app/docs/layout.tsx
Normal file
63
apps/web/src/app/docs/layout.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import type { Metadata } from "next";
|
||||
import { DocsSidebar } from "./docs-sidebar";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
default: "Documentation",
|
||||
template: "%s | AgentLens Docs",
|
||||
},
|
||||
description:
|
||||
"AgentLens documentation — learn how to instrument, trace, and observe your AI agents.",
|
||||
};
|
||||
|
||||
export default function DocsLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<div className="min-h-screen bg-neutral-950">
|
||||
<header className="sticky top-0 z-50 border-b border-neutral-800/50 bg-neutral-950/80 backdrop-blur-md">
|
||||
<div className="max-w-[90rem] mx-auto flex items-center justify-between h-14 px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex items-center gap-6">
|
||||
<a href="/" className="flex items-center gap-2 text-neutral-100 hover:text-emerald-400 transition-colors">
|
||||
<div className="w-7 h-7 rounded bg-gradient-to-br from-emerald-400 to-emerald-600 flex items-center justify-center">
|
||||
<svg className="w-4 h-4 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="font-semibold text-sm">AgentLens</span>
|
||||
</a>
|
||||
<span className="text-neutral-600">/</span>
|
||||
<a href="/docs" className="text-sm text-neutral-400 hover:text-neutral-200 transition-colors">
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<a
|
||||
href="/dashboard"
|
||||
className="text-sm text-neutral-400 hover:text-neutral-200 transition-colors"
|
||||
>
|
||||
Dashboard
|
||||
</a>
|
||||
<a
|
||||
href="https://gitea.repi.fun/repi/agentlens"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-neutral-400 hover:text-neutral-200 transition-colors"
|
||||
>
|
||||
Source
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="max-w-[90rem] mx-auto flex">
|
||||
<DocsSidebar />
|
||||
<main className="flex-1 min-w-0 px-4 sm:px-8 lg:px-12 py-10 lg:py-12">
|
||||
<div className="max-w-3xl">{children}</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
230
apps/web/src/app/docs/opencode-plugin/page.tsx
Normal file
230
apps/web/src/app/docs/opencode-plugin/page.tsx
Normal file
@@ -0,0 +1,230 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "OpenCode Plugin",
|
||||
description:
|
||||
"Capture OpenCode sessions including tool calls, LLM calls, file edits, and git diffs with the AgentLens OpenCode plugin.",
|
||||
};
|
||||
|
||||
function CodeBlock({ children, title }: { children: string; title?: string }) {
|
||||
return (
|
||||
<div className="rounded-xl overflow-hidden border border-neutral-800 bg-neutral-900/50 my-4">
|
||||
{title && (
|
||||
<div className="px-4 py-2.5 border-b border-neutral-800 text-xs text-neutral-500 font-mono">
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
<pre className="p-4 overflow-x-auto text-sm leading-relaxed">
|
||||
<code className="text-neutral-300">{children}</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function OpenCodePluginPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold tracking-tight mb-4">
|
||||
OpenCode Plugin
|
||||
</h1>
|
||||
<p className="text-lg text-neutral-400 mb-10 leading-relaxed">
|
||||
The AgentLens OpenCode plugin captures everything that happens during an
|
||||
OpenCode coding session and sends it as structured traces to your
|
||||
AgentLens instance.
|
||||
</p>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Installation</h2>
|
||||
<CodeBlock title="terminal">{`npm install opencode-agentlens`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Configuration</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
Add the plugin to your <code className="text-emerald-400 font-mono text-xs bg-emerald-500/5 px-1.5 py-0.5 rounded">opencode.json</code> configuration file:
|
||||
</p>
|
||||
<CodeBlock title="opencode.json">{`{
|
||||
"plugin": ["opencode-agentlens"]
|
||||
}`}</CodeBlock>
|
||||
<p className="text-neutral-400 leading-relaxed mt-4 mb-4">
|
||||
Set the required environment variables:
|
||||
</p>
|
||||
<CodeBlock title="terminal">{`export AGENTLENS_API_KEY="your-api-key"
|
||||
export AGENTLENS_ENDPOINT="https://agentlens.vectry.tech"`}</CodeBlock>
|
||||
|
||||
<p className="text-neutral-400 leading-relaxed mt-4">
|
||||
You can also add these to a <code className="text-emerald-400 font-mono text-xs bg-emerald-500/5 px-1.5 py-0.5 rounded">.env</code> file in your project root.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">What gets captured</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-6">
|
||||
Every OpenCode session becomes a trace with nested spans and events
|
||||
for each action taken during the session:
|
||||
</p>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="p-4 rounded-lg border border-neutral-800/50 bg-neutral-900/30">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className="px-2 py-0.5 rounded text-xs font-mono bg-emerald-500/10 text-emerald-400 border border-emerald-500/20">
|
||||
AGENT span
|
||||
</span>
|
||||
<span className="text-sm font-medium text-neutral-200">
|
||||
Sessions
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-neutral-500">
|
||||
Each OpenCode session is captured as a top-level AGENT span.
|
||||
Includes session ID, start time, end time, and overall status.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-lg border border-neutral-800/50 bg-neutral-900/30">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className="px-2 py-0.5 rounded text-xs font-mono bg-emerald-500/10 text-emerald-400 border border-emerald-500/20">
|
||||
LLM_CALL span
|
||||
</span>
|
||||
<span className="text-sm font-medium text-neutral-200">
|
||||
LLM calls
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-neutral-500">
|
||||
Every call to an LLM provider (Claude, GPT, etc.) is recorded with
|
||||
the full prompt, response, token counts, and cost.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-lg border border-neutral-800/50 bg-neutral-900/30">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className="px-2 py-0.5 rounded text-xs font-mono bg-emerald-500/10 text-emerald-400 border border-emerald-500/20">
|
||||
TOOL_CALL span
|
||||
</span>
|
||||
<span className="text-sm font-medium text-neutral-200">
|
||||
Tool calls
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-neutral-500">
|
||||
Tool invocations including file reads, writes, shell commands,
|
||||
search operations, and MCP tool calls. Captures input arguments
|
||||
and outputs.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-lg border border-neutral-800/50 bg-neutral-900/30">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className="px-2 py-0.5 rounded text-xs font-mono bg-emerald-500/10 text-emerald-400 border border-emerald-500/20">
|
||||
TOOL_SELECTION decision
|
||||
</span>
|
||||
<span className="text-sm font-medium text-neutral-200">
|
||||
Permissions
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-neutral-500">
|
||||
Permission requests and grants are captured as decision points,
|
||||
showing what the agent asked to do and whether it was allowed.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-lg border border-neutral-800/50 bg-neutral-900/30">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className="px-2 py-0.5 rounded text-xs font-mono bg-emerald-500/10 text-emerald-400 border border-emerald-500/20">
|
||||
CUSTOM span
|
||||
</span>
|
||||
<span className="text-sm font-medium text-neutral-200">
|
||||
File edits
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-neutral-500">
|
||||
Every file creation, modification, and deletion is tracked with
|
||||
before/after content diffs.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-lg border border-neutral-800/50 bg-neutral-900/30">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className="px-2 py-0.5 rounded text-xs font-mono bg-emerald-500/10 text-emerald-400 border border-emerald-500/20">
|
||||
CUSTOM event
|
||||
</span>
|
||||
<span className="text-sm font-medium text-neutral-200">
|
||||
Git diffs
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-neutral-500">
|
||||
Git operations (commits, diffs, branch changes) are captured as
|
||||
events with the full diff content.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Trace structure</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
A typical OpenCode session trace looks like this:
|
||||
</p>
|
||||
<CodeBlock>{`Trace: "opencode-session-abc123"
|
||||
|
|
||||
+-- Span: "session" (AGENT)
|
||||
| +-- Span: "read-file: src/main.ts" (TOOL_CALL)
|
||||
| +-- Span: "llm-call: claude-sonnet" (LLM_CALL)
|
||||
| | Decision: TOOL_SELECTION -> chose "edit-file" over "write-file"
|
||||
| +-- Span: "edit-file: src/main.ts" (TOOL_CALL)
|
||||
| +-- Span: "llm-call: claude-sonnet" (LLM_CALL)
|
||||
| +-- Span: "bash: npm test" (TOOL_CALL)
|
||||
| +-- Event: "git-diff" (CUSTOM)
|
||||
| +-- Span: "bash: git commit" (TOOL_CALL)`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Environment variables</h2>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Variable</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Required</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">AGENTLENS_API_KEY</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">API key for authentication</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">AGENTLENS_ENDPOINT</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2">AgentLens server URL</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">AGENTLENS_ENABLED</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Set to "false" to disable (default: "true")</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">AGENTLENS_SESSION_TAGS</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2">Comma-separated tags to add to all session traces</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold mb-4">Filtering sensitive data</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
By default, the plugin captures full file contents and command outputs.
|
||||
To filter sensitive data, set the <code className="text-emerald-400 font-mono text-xs bg-emerald-500/5 px-1.5 py-0.5 rounded">AGENTLENS_REDACT_PATTERNS</code> environment variable with a comma-separated list of regex patterns:
|
||||
</p>
|
||||
<CodeBlock title="terminal">{`export AGENTLENS_REDACT_PATTERNS="password=.*,API_KEY=.*,Bearer .*"`}</CodeBlock>
|
||||
<p className="text-neutral-400 leading-relaxed mt-4">
|
||||
Matched content is replaced with <code className="text-emerald-400 font-mono text-xs bg-emerald-500/5 px-1.5 py-0.5 rounded">[REDACTED]</code> before
|
||||
being sent to the server.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
131
apps/web/src/app/docs/page.tsx
Normal file
131
apps/web/src/app/docs/page.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Documentation",
|
||||
description:
|
||||
"AgentLens documentation — instrument, trace, and observe your AI agents with full decision visibility.",
|
||||
};
|
||||
|
||||
const sections = [
|
||||
{
|
||||
heading: "Getting Started",
|
||||
items: [
|
||||
{
|
||||
title: "Quick Start",
|
||||
href: "/docs/getting-started",
|
||||
description:
|
||||
"Install the SDK, initialize AgentLens, and send your first trace in under five minutes.",
|
||||
},
|
||||
{
|
||||
title: "Core Concepts",
|
||||
href: "/docs/concepts",
|
||||
description:
|
||||
"Understand Traces, Spans, Decision Points, and Events — the four building blocks of AgentLens.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "SDKs",
|
||||
items: [
|
||||
{
|
||||
title: "Python SDK",
|
||||
href: "/docs/python-sdk",
|
||||
description:
|
||||
"Full reference for the Python SDK: init(), @trace decorator, log_decision(), TraceContext, and configuration.",
|
||||
},
|
||||
{
|
||||
title: "TypeScript SDK",
|
||||
href: "/docs/typescript-sdk",
|
||||
description:
|
||||
"Full reference for the TypeScript SDK: init(), TraceBuilder API, createDecision(), and shutdown().",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Integrations",
|
||||
items: [
|
||||
{
|
||||
title: "OpenAI",
|
||||
href: "/docs/integrations/openai",
|
||||
description:
|
||||
"Auto-trace all OpenAI API calls with a single wrapper. Captures model, tokens, cost, and latency.",
|
||||
},
|
||||
{
|
||||
title: "Anthropic",
|
||||
href: "/docs/integrations/anthropic",
|
||||
description:
|
||||
"Wrap the Anthropic client to automatically trace Claude API calls with full metadata capture.",
|
||||
},
|
||||
{
|
||||
title: "LangChain",
|
||||
href: "/docs/integrations/langchain",
|
||||
description:
|
||||
"Use the AgentLensCallbackHandler to trace LangChain chains, agents, and tool invocations.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Tools & Deployment",
|
||||
items: [
|
||||
{
|
||||
title: "OpenCode Plugin",
|
||||
href: "/docs/opencode-plugin",
|
||||
description:
|
||||
"Capture OpenCode sessions including tool calls, LLM calls, file edits, and git diffs automatically.",
|
||||
},
|
||||
{
|
||||
title: "REST API Reference",
|
||||
href: "/docs/api-reference",
|
||||
description:
|
||||
"Complete contract for POST /api/traces and GET /api/traces including payload shapes and error codes.",
|
||||
},
|
||||
{
|
||||
title: "Self-Hosting",
|
||||
href: "/docs/self-hosting",
|
||||
description:
|
||||
"Deploy AgentLens with Docker or from source. Configure database, API keys, and environment variables.",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function DocsPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold tracking-tight mb-4">
|
||||
AgentLens Documentation
|
||||
</h1>
|
||||
<p className="text-lg text-neutral-400 mb-12 leading-relaxed max-w-2xl">
|
||||
AgentLens is an open-source agent observability platform that traces
|
||||
decisions, not just API calls. These docs cover everything from initial
|
||||
setup to advanced self-hosting.
|
||||
</p>
|
||||
|
||||
<div className="space-y-12">
|
||||
{sections.map((section) => (
|
||||
<div key={section.heading}>
|
||||
<h2 className="text-xs font-semibold uppercase tracking-wider text-neutral-500 mb-4">
|
||||
{section.heading}
|
||||
</h2>
|
||||
<div className="grid sm:grid-cols-2 gap-4">
|
||||
{section.items.map((item) => (
|
||||
<a
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className="group block p-5 rounded-xl border border-neutral-800/50 bg-neutral-900/30 hover:border-emerald-500/30 hover:bg-neutral-900/60 transition-all duration-200"
|
||||
>
|
||||
<h3 className="text-base font-semibold text-neutral-100 group-hover:text-emerald-400 transition-colors mb-1.5">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-sm text-neutral-500 leading-relaxed">
|
||||
{item.description}
|
||||
</p>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
327
apps/web/src/app/docs/python-sdk/page.tsx
Normal file
327
apps/web/src/app/docs/python-sdk/page.tsx
Normal file
@@ -0,0 +1,327 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Python SDK",
|
||||
description:
|
||||
"Full reference for the AgentLens Python SDK: init(), @trace decorator, log_decision(), TraceContext, and configuration.",
|
||||
};
|
||||
|
||||
function CodeBlock({ children, title }: { children: string; title?: string }) {
|
||||
return (
|
||||
<div className="rounded-xl overflow-hidden border border-neutral-800 bg-neutral-900/50 my-4">
|
||||
{title && (
|
||||
<div className="px-4 py-2.5 border-b border-neutral-800 text-xs text-neutral-500 font-mono">
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
<pre className="p-4 overflow-x-auto text-sm leading-relaxed">
|
||||
<code className="text-neutral-300">{children}</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ApiSection({
|
||||
name,
|
||||
signature,
|
||||
description,
|
||||
children,
|
||||
}: {
|
||||
name: string;
|
||||
signature: string;
|
||||
description: string;
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="mb-10 pb-10 border-b border-neutral-800/50 last:border-0">
|
||||
<h3 className="text-xl font-semibold mb-1">{name}</h3>
|
||||
<div className="px-3 py-1.5 rounded-lg bg-neutral-900/50 border border-neutral-800/50 inline-block mb-3">
|
||||
<code className="text-sm font-mono text-emerald-400">{signature}</code>
|
||||
</div>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">{description}</p>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function PythonSdkPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold tracking-tight mb-4">Python SDK</h1>
|
||||
<p className="text-lg text-neutral-400 mb-4 leading-relaxed">
|
||||
The AgentLens Python SDK provides decorators, context managers, and
|
||||
helper functions to instrument your AI agents.
|
||||
</p>
|
||||
|
||||
<div className="px-4 py-3 rounded-lg bg-neutral-900/50 border border-neutral-800/50 mb-10">
|
||||
<code className="text-sm font-mono text-emerald-400">
|
||||
pip install vectry-agentlens
|
||||
</code>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-semibold mb-6">API Reference</h2>
|
||||
|
||||
<ApiSection
|
||||
name="init()"
|
||||
signature="agentlens.init(api_key, endpoint, *, flush_interval=5.0, max_batch_size=100, enabled=True)"
|
||||
description="Initialize the AgentLens SDK. Must be called before any tracing functions. Typically called once at application startup."
|
||||
>
|
||||
<h4 className="text-sm font-medium text-neutral-300 mb-2">
|
||||
Parameters
|
||||
</h4>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Parameter</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Type</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Default</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">api_key</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">str</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">required</td>
|
||||
<td className="py-2">Your AgentLens API key</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">endpoint</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">str</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">required</td>
|
||||
<td className="py-2">AgentLens server URL</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">flush_interval</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">float</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">5.0</td>
|
||||
<td className="py-2">Seconds between automatic flushes</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">max_batch_size</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">int</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">100</td>
|
||||
<td className="py-2">Max traces per batch request</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">enabled</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">bool</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">True</td>
|
||||
<td className="py-2">Set to False to disable tracing globally</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<CodeBlock title="example.py">{`import agentlens
|
||||
|
||||
agentlens.init(
|
||||
api_key="al_key_abc123",
|
||||
endpoint="https://agentlens.vectry.tech",
|
||||
flush_interval=10.0,
|
||||
max_batch_size=50,
|
||||
)`}</CodeBlock>
|
||||
</ApiSection>
|
||||
|
||||
<ApiSection
|
||||
name="@trace"
|
||||
signature='@agentlens.trace(name=None, tags=None, metadata=None)'
|
||||
description="Decorator that wraps a function in a trace. The trace starts when the function is called and ends when it returns or raises. Works with both sync and async functions."
|
||||
>
|
||||
<h4 className="text-sm font-medium text-neutral-300 mb-2">
|
||||
Parameters
|
||||
</h4>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Parameter</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Type</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">name</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">str | None</td>
|
||||
<td className="py-2">Trace name. Defaults to the function name.</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">tags</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">list[str] | None</td>
|
||||
<td className="py-2">Tags to attach to the trace</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">metadata</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">dict | None</td>
|
||||
<td className="py-2">Arbitrary metadata dict</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<CodeBlock title="decorator.py">{`from agentlens import trace
|
||||
|
||||
@trace(name="research-agent", tags=["research", "v2"])
|
||||
async def research(topic: str) -> str:
|
||||
result = await search(topic)
|
||||
summary = await summarize(result)
|
||||
return summary
|
||||
|
||||
# Can also be used without arguments
|
||||
@trace
|
||||
def simple_agent(prompt: str) -> str:
|
||||
return call_llm(prompt)`}</CodeBlock>
|
||||
</ApiSection>
|
||||
|
||||
<ApiSection
|
||||
name="log_decision()"
|
||||
signature="agentlens.log_decision(type, chosen, alternatives, *, reasoning=None, context_snapshot=None)"
|
||||
description="Log a decision point within the current trace context. Must be called from within a @trace-decorated function."
|
||||
>
|
||||
<h4 className="text-sm font-medium text-neutral-300 mb-2">
|
||||
Parameters
|
||||
</h4>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Parameter</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Type</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">type</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">str</td>
|
||||
<td className="py-2">One of: TOOL_SELECTION, ROUTING, RETRY, ESCALATION, MEMORY_RETRIEVAL, PLANNING, CUSTOM</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">chosen</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">dict</td>
|
||||
<td className="py-2">What was chosen</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">alternatives</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">list[dict]</td>
|
||||
<td className="py-2">What else was considered</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">reasoning</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">str | None</td>
|
||||
<td className="py-2">Why this choice was made</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">context_snapshot</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">dict | None</td>
|
||||
<td className="py-2">Snapshot of context at decision time</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<CodeBlock title="decisions.py">{`import agentlens
|
||||
from agentlens import trace
|
||||
|
||||
@trace(name="routing-agent")
|
||||
async def route_request(user_input: str):
|
||||
intent = classify_intent(user_input)
|
||||
|
||||
agentlens.log_decision(
|
||||
type="ROUTING",
|
||||
chosen={"handler": "refund", "confidence": 0.92},
|
||||
alternatives=[
|
||||
{"handler": "faq", "confidence": 0.65},
|
||||
{"handler": "escalate", "confidence": 0.23},
|
||||
],
|
||||
reasoning="High confidence refund intent detected",
|
||||
context_snapshot={"intent": intent, "input_length": len(user_input)},
|
||||
)
|
||||
|
||||
return await handle_refund(user_input)`}</CodeBlock>
|
||||
</ApiSection>
|
||||
|
||||
<ApiSection
|
||||
name="TraceContext"
|
||||
signature="agentlens.TraceContext"
|
||||
description="Context manager for manual trace lifecycle control. Use this when the @trace decorator does not fit your workflow."
|
||||
>
|
||||
<CodeBlock title="context.py">{`import agentlens
|
||||
|
||||
async def process_batch(items: list[str]):
|
||||
for item in items:
|
||||
ctx = agentlens.TraceContext(
|
||||
name=f"process-{item}",
|
||||
tags=["batch"],
|
||||
)
|
||||
ctx.start()
|
||||
|
||||
try:
|
||||
result = await process(item)
|
||||
ctx.add_span(
|
||||
name="process",
|
||||
type="CUSTOM",
|
||||
input={"item": item},
|
||||
output={"result": result},
|
||||
status="COMPLETED",
|
||||
)
|
||||
ctx.end(status="COMPLETED")
|
||||
except Exception as e:
|
||||
ctx.add_event(type="ERROR", name=str(e))
|
||||
ctx.end(status="ERROR")`}</CodeBlock>
|
||||
</ApiSection>
|
||||
|
||||
<ApiSection
|
||||
name="shutdown()"
|
||||
signature="agentlens.shutdown(timeout=10.0)"
|
||||
description="Flush all pending traces and shut down the background sender. Call this before your application exits to avoid losing data."
|
||||
>
|
||||
<CodeBlock title="shutdown.py">{`import agentlens
|
||||
import atexit
|
||||
|
||||
agentlens.init(api_key="...", endpoint="...")
|
||||
|
||||
# Register shutdown hook
|
||||
atexit.register(agentlens.shutdown)
|
||||
|
||||
# Or call manually
|
||||
agentlens.shutdown(timeout=30.0)`}</CodeBlock>
|
||||
</ApiSection>
|
||||
|
||||
<section className="mt-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Configuration</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
The SDK can also be configured via environment variables. These take
|
||||
precedence over values passed to <code className="text-emerald-400 font-mono text-xs bg-emerald-500/5 px-1.5 py-0.5 rounded">init()</code>.
|
||||
</p>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Variable</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">AGENTLENS_API_KEY</td>
|
||||
<td className="py-2">API key for authentication</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">AGENTLENS_ENDPOINT</td>
|
||||
<td className="py-2">Server URL</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">AGENTLENS_ENABLED</td>
|
||||
<td className="py-2">Set to "false" to disable tracing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">AGENTLENS_FLUSH_INTERVAL</td>
|
||||
<td className="py-2">Flush interval in seconds</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
255
apps/web/src/app/docs/self-hosting/page.tsx
Normal file
255
apps/web/src/app/docs/self-hosting/page.tsx
Normal file
@@ -0,0 +1,255 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Self-Hosting",
|
||||
description:
|
||||
"Deploy AgentLens with Docker or from source. Configure database, API keys, and environment variables.",
|
||||
};
|
||||
|
||||
function CodeBlock({ children, title }: { children: string; title?: string }) {
|
||||
return (
|
||||
<div className="rounded-xl overflow-hidden border border-neutral-800 bg-neutral-900/50 my-4">
|
||||
{title && (
|
||||
<div className="px-4 py-2.5 border-b border-neutral-800 text-xs text-neutral-500 font-mono">
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
<pre className="p-4 overflow-x-auto text-sm leading-relaxed">
|
||||
<code className="text-neutral-300">{children}</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SelfHostingPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold tracking-tight mb-4">Self-Hosting</h1>
|
||||
<p className="text-lg text-neutral-400 mb-10 leading-relaxed">
|
||||
AgentLens is open source and designed to be self-hosted. You can deploy
|
||||
it with Docker in minutes, or run from source for development.
|
||||
</p>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Quick start with Docker</h2>
|
||||
<CodeBlock title="terminal">{`git clone https://gitea.repi.fun/repi/agentlens
|
||||
cd agentlens
|
||||
docker build -t agentlens .
|
||||
docker run -p 3000:3000 \\
|
||||
-e DATABASE_URL="postgresql://user:pass@host:5432/agentlens" \\
|
||||
-e AGENTLENS_API_KEY="your-secret-key" \\
|
||||
agentlens`}</CodeBlock>
|
||||
<p className="text-neutral-400 leading-relaxed mt-4">
|
||||
The dashboard will be available at{" "}
|
||||
<code className="text-emerald-400 font-mono text-xs bg-emerald-500/5 px-1.5 py-0.5 rounded">
|
||||
http://localhost:3000
|
||||
</code>{" "}
|
||||
and the API at{" "}
|
||||
<code className="text-emerald-400 font-mono text-xs bg-emerald-500/5 px-1.5 py-0.5 rounded">
|
||||
http://localhost:3000/api/traces
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Docker Compose</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
For a complete setup with PostgreSQL included:
|
||||
</p>
|
||||
<CodeBlock title="docker-compose.yml">{`version: "3.8"
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_USER: agentlens
|
||||
POSTGRES_PASSWORD: agentlens
|
||||
POSTGRES_DB: agentlens
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432"
|
||||
|
||||
app:
|
||||
build: .
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
DATABASE_URL: "postgresql://agentlens:agentlens@db:5432/agentlens"
|
||||
AGENTLENS_API_KEY: "your-secret-key"
|
||||
PORT: "3000"
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
volumes:
|
||||
pgdata:`}</CodeBlock>
|
||||
<CodeBlock title="terminal">{`docker compose up -d`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Running from source</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
For development or when you need to customize AgentLens:
|
||||
</p>
|
||||
<CodeBlock title="terminal">{`git clone https://gitea.repi.fun/repi/agentlens
|
||||
cd agentlens
|
||||
|
||||
# Install dependencies (uses npm workspaces)
|
||||
npm install
|
||||
|
||||
# Set up the database
|
||||
cp apps/web/.env.example apps/web/.env
|
||||
# Edit .env with your DATABASE_URL
|
||||
|
||||
# Generate Prisma client and push schema
|
||||
npm run db:generate --workspace=@agentlens/web
|
||||
npm run db:push --workspace=@agentlens/web
|
||||
|
||||
# Start the development server
|
||||
npm run dev --workspace=@agentlens/web`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Environment variables</h2>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Variable</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Required</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Default</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">DATABASE_URL</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">-</td>
|
||||
<td className="py-2">PostgreSQL connection string</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">AGENTLENS_API_KEY</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Yes</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">-</td>
|
||||
<td className="py-2">API key that SDKs must present to ingest traces</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">PORT</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">3000</td>
|
||||
<td className="py-2">HTTP port the server listens on</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">NODE_ENV</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">production</td>
|
||||
<td className="py-2">Set to "development" for dev mode</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">NEXTAUTH_SECRET</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">No</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">-</td>
|
||||
<td className="py-2">Secret for session signing (if auth is enabled)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Database setup</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
AgentLens uses PostgreSQL with Prisma ORM. The database schema is
|
||||
managed via Prisma migrations.
|
||||
</p>
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-2">
|
||||
Connection string format
|
||||
</h3>
|
||||
<CodeBlock>{`postgresql://USER:PASSWORD@HOST:PORT/DATABASE`}</CodeBlock>
|
||||
|
||||
<h3 className="text-lg font-medium text-neutral-200 mb-2 mt-6">
|
||||
Running migrations
|
||||
</h3>
|
||||
<CodeBlock title="terminal">{`# Push schema to database (development)
|
||||
npm run db:push --workspace=@agentlens/web
|
||||
|
||||
# Run migrations (production)
|
||||
npm run db:migrate --workspace=@agentlens/web`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Reverse proxy setup</h2>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">
|
||||
For production deployments behind nginx or Caddy:
|
||||
</p>
|
||||
<CodeBlock title="Caddyfile">{`agentlens.yourdomain.com {
|
||||
reverse_proxy localhost:3000
|
||||
}`}</CodeBlock>
|
||||
<CodeBlock title="nginx.conf">{`server {
|
||||
listen 443 ssl;
|
||||
server_name agentlens.yourdomain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Updating</h2>
|
||||
<CodeBlock title="terminal">{`# Pull latest changes
|
||||
cd agentlens
|
||||
git pull origin main
|
||||
|
||||
# Rebuild
|
||||
docker build -t agentlens .
|
||||
|
||||
# Restart with new image
|
||||
docker compose up -d`}</CodeBlock>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold mb-4">Resource requirements</h2>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Component</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Minimum</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Recommended</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 text-neutral-400">CPU</td>
|
||||
<td className="py-2 pr-4">1 core</td>
|
||||
<td className="py-2">2+ cores</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 text-neutral-400">Memory</td>
|
||||
<td className="py-2 pr-4">512 MB</td>
|
||||
<td className="py-2">1 GB+</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 text-neutral-400">Disk</td>
|
||||
<td className="py-2 pr-4">1 GB</td>
|
||||
<td className="py-2">10 GB+ (depends on trace volume)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 text-neutral-400">PostgreSQL</td>
|
||||
<td className="py-2 pr-4">14+</td>
|
||||
<td className="py-2">16+</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
301
apps/web/src/app/docs/typescript-sdk/page.tsx
Normal file
301
apps/web/src/app/docs/typescript-sdk/page.tsx
Normal file
@@ -0,0 +1,301 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "TypeScript SDK",
|
||||
description:
|
||||
"Full reference for the AgentLens TypeScript SDK: init(), TraceBuilder, createDecision(), and shutdown().",
|
||||
};
|
||||
|
||||
function CodeBlock({ children, title }: { children: string; title?: string }) {
|
||||
return (
|
||||
<div className="rounded-xl overflow-hidden border border-neutral-800 bg-neutral-900/50 my-4">
|
||||
{title && (
|
||||
<div className="px-4 py-2.5 border-b border-neutral-800 text-xs text-neutral-500 font-mono">
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
<pre className="p-4 overflow-x-auto text-sm leading-relaxed">
|
||||
<code className="text-neutral-300">{children}</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ApiSection({
|
||||
name,
|
||||
signature,
|
||||
description,
|
||||
children,
|
||||
}: {
|
||||
name: string;
|
||||
signature: string;
|
||||
description: string;
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="mb-10 pb-10 border-b border-neutral-800/50 last:border-0">
|
||||
<h3 className="text-xl font-semibold mb-1">{name}</h3>
|
||||
<div className="px-3 py-1.5 rounded-lg bg-neutral-900/50 border border-neutral-800/50 inline-block mb-3">
|
||||
<code className="text-sm font-mono text-emerald-400">{signature}</code>
|
||||
</div>
|
||||
<p className="text-neutral-400 leading-relaxed mb-4">{description}</p>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TypeScriptSdkPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold tracking-tight mb-4">
|
||||
TypeScript SDK
|
||||
</h1>
|
||||
<p className="text-lg text-neutral-400 mb-4 leading-relaxed">
|
||||
The AgentLens TypeScript SDK provides a builder-based API for
|
||||
constructing and sending traces from Node.js and edge runtimes.
|
||||
</p>
|
||||
|
||||
<div className="px-4 py-3 rounded-lg bg-neutral-900/50 border border-neutral-800/50 mb-10">
|
||||
<code className="text-sm font-mono text-emerald-400">
|
||||
npm install agentlens-sdk
|
||||
</code>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-semibold mb-6">API Reference</h2>
|
||||
|
||||
<ApiSection
|
||||
name="init()"
|
||||
signature='init({ apiKey, endpoint, flushInterval?, maxBatchSize?, enabled? })'
|
||||
description="Initialize the SDK. Must be called once before creating any traces."
|
||||
>
|
||||
<h4 className="text-sm font-medium text-neutral-300 mb-2">
|
||||
Options
|
||||
</h4>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Property</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Type</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Default</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">apiKey</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">required</td>
|
||||
<td className="py-2">Your AgentLens API key</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">endpoint</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">required</td>
|
||||
<td className="py-2">AgentLens server URL</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">flushInterval</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">number</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">5000</td>
|
||||
<td className="py-2">Milliseconds between flushes</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">maxBatchSize</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">number</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">100</td>
|
||||
<td className="py-2">Max traces per batch</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">enabled</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">boolean</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">true</td>
|
||||
<td className="py-2">Toggle tracing on/off</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<CodeBlock title="init.ts">{`import { init } from "agentlens-sdk";
|
||||
|
||||
init({
|
||||
apiKey: process.env.AGENTLENS_API_KEY!,
|
||||
endpoint: "https://agentlens.vectry.tech",
|
||||
flushInterval: 10000,
|
||||
});`}</CodeBlock>
|
||||
</ApiSection>
|
||||
|
||||
<ApiSection
|
||||
name="TraceBuilder"
|
||||
signature='new TraceBuilder(name, options?)'
|
||||
description="Builder for constructing a trace incrementally. Add spans, decision points, and events, then call end() to finalize and send."
|
||||
>
|
||||
<h4 className="text-sm font-medium text-neutral-300 mb-2">
|
||||
Constructor options
|
||||
</h4>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Property</th>
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Type</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">tags</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string[]</td>
|
||||
<td className="py-2">Tags for this trace</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">sessionId</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">string</td>
|
||||
<td className="py-2">Group traces into a session</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">metadata</td>
|
||||
<td className="py-2 pr-4 text-neutral-500">Record<string, unknown></td>
|
||||
<td className="py-2">Arbitrary metadata</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h4 className="text-sm font-medium text-neutral-300 mb-2 mt-6">
|
||||
Methods
|
||||
</h4>
|
||||
|
||||
<div className="space-y-4 mb-6">
|
||||
<div className="p-3 rounded-lg border border-neutral-800/50 bg-neutral-900/30">
|
||||
<code className="text-sm font-mono text-emerald-400">
|
||||
addSpan(span: SpanInput): string
|
||||
</code>
|
||||
<p className="text-xs text-neutral-500 mt-1">
|
||||
Add a span to the trace. Returns the generated span ID. Pass <code className="text-emerald-400/80 font-mono bg-emerald-500/5 px-1 rounded">parentSpanId</code> to nest spans.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-3 rounded-lg border border-neutral-800/50 bg-neutral-900/30">
|
||||
<code className="text-sm font-mono text-emerald-400">
|
||||
addDecision(decision: DecisionInput): string
|
||||
</code>
|
||||
<p className="text-xs text-neutral-500 mt-1">
|
||||
Add a decision point. Returns the generated decision ID.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-3 rounded-lg border border-neutral-800/50 bg-neutral-900/30">
|
||||
<code className="text-sm font-mono text-emerald-400">
|
||||
addEvent(event: EventInput): string
|
||||
</code>
|
||||
<p className="text-xs text-neutral-500 mt-1">
|
||||
Add an event to the trace. Returns the generated event ID.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-3 rounded-lg border border-neutral-800/50 bg-neutral-900/30">
|
||||
<code className="text-sm font-mono text-emerald-400">
|
||||
end(status?: "COMPLETED" | "ERROR"): Promise<void>
|
||||
</code>
|
||||
<p className="text-xs text-neutral-500 mt-1">
|
||||
Finalize and send the trace. Defaults to COMPLETED.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CodeBlock title="trace-builder.ts">{`import { TraceBuilder } from "agentlens-sdk";
|
||||
|
||||
const trace = new TraceBuilder("customer-support", {
|
||||
tags: ["support", "v2"],
|
||||
sessionId: "session-abc",
|
||||
});
|
||||
|
||||
const agentSpan = trace.addSpan({
|
||||
name: "classify-intent",
|
||||
type: "LLM_CALL",
|
||||
input: { messages: [{ role: "user", content: "I need a refund" }] },
|
||||
output: { intent: "refund", confidence: 0.95 },
|
||||
status: "COMPLETED",
|
||||
tokenCount: 150,
|
||||
costUsd: 0.002,
|
||||
durationMs: 340,
|
||||
});
|
||||
|
||||
trace.addDecision({
|
||||
type: "ROUTING",
|
||||
chosen: { handler: "refund-flow" },
|
||||
alternatives: [{ handler: "faq-flow" }, { handler: "escalate" }],
|
||||
reasoning: "High confidence refund intent",
|
||||
parentSpanId: agentSpan,
|
||||
});
|
||||
|
||||
trace.addSpan({
|
||||
name: "process-refund",
|
||||
type: "TOOL_CALL",
|
||||
input: { orderId: "ord-123" },
|
||||
output: { success: true },
|
||||
status: "COMPLETED",
|
||||
parentSpanId: agentSpan,
|
||||
});
|
||||
|
||||
await trace.end();`}</CodeBlock>
|
||||
</ApiSection>
|
||||
|
||||
<ApiSection
|
||||
name="createDecision()"
|
||||
signature='createDecision(type, chosen, alternatives, options?)'
|
||||
description="Standalone helper to create a decision point outside of a TraceBuilder. Useful when building traces from raw data."
|
||||
>
|
||||
<CodeBlock title="standalone.ts">{`import { createDecision } from "agentlens-sdk";
|
||||
|
||||
const decision = createDecision(
|
||||
"TOOL_SELECTION",
|
||||
{ tool: "calculator", confidence: 0.88 },
|
||||
[
|
||||
{ tool: "web_search", confidence: 0.52 },
|
||||
{ tool: "code_exec", confidence: 0.34 },
|
||||
],
|
||||
{ reasoning: "Math expression detected in input" }
|
||||
);`}</CodeBlock>
|
||||
</ApiSection>
|
||||
|
||||
<ApiSection
|
||||
name="shutdown()"
|
||||
signature="shutdown(timeout?: number): Promise<void>"
|
||||
description="Flush all pending traces and tear down the background sender. Default timeout is 10 seconds."
|
||||
>
|
||||
<CodeBlock title="shutdown.ts">{`import { shutdown } from "agentlens-sdk";
|
||||
|
||||
process.on("SIGTERM", async () => {
|
||||
await shutdown(30000);
|
||||
process.exit(0);
|
||||
});`}</CodeBlock>
|
||||
</ApiSection>
|
||||
|
||||
<section className="mt-12">
|
||||
<h2 className="text-2xl font-semibold mb-4">Environment Variables</h2>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-neutral-800">
|
||||
<th className="text-left py-2 pr-4 text-neutral-400 font-medium">Variable</th>
|
||||
<th className="text-left py-2 text-neutral-400 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-neutral-300">
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">AGENTLENS_API_KEY</td>
|
||||
<td className="py-2">API key (overrides init param)</td>
|
||||
</tr>
|
||||
<tr className="border-b border-neutral-800/50">
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">AGENTLENS_ENDPOINT</td>
|
||||
<td className="py-2">Server URL (overrides init param)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 pr-4 font-mono text-emerald-400 text-xs">AGENTLENS_ENABLED</td>
|
||||
<td className="py-2">Set to "false" to disable</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -467,12 +467,27 @@ function TokenUsageGauge({ trace }: { trace: Trace }) {
|
||||
null;
|
||||
|
||||
const modelContextWindows: Record<string, number> = {
|
||||
"gpt-5.2": 128000,
|
||||
"gpt-5.1": 128000,
|
||||
"gpt-5": 128000,
|
||||
"gpt-5-mini": 128000,
|
||||
"gpt-5-nano": 128000,
|
||||
"gpt-4.1": 1047576,
|
||||
"gpt-4.1-mini": 1047576,
|
||||
"gpt-4.1-nano": 1047576,
|
||||
"o3": 200000,
|
||||
"o3-mini": 200000,
|
||||
"o4-mini": 200000,
|
||||
"gpt-4": 8192,
|
||||
"gpt-4-32k": 32768,
|
||||
"gpt-4-turbo": 128000,
|
||||
"gpt-4o": 128000,
|
||||
"gpt-4o-mini": 128000,
|
||||
"gpt-3.5-turbo": 16385,
|
||||
"claude-opus-4-6": 200000,
|
||||
"claude-4.5-opus": 200000,
|
||||
"claude-4.5-sonnet": 200000,
|
||||
"claude-4.5-haiku": 200000,
|
||||
"claude-3-opus": 200000,
|
||||
"claude-3-sonnet": 200000,
|
||||
"claude-3-haiku": 200000,
|
||||
|
||||
@@ -52,13 +52,32 @@ export function extractToolMetadata(
|
||||
}
|
||||
|
||||
const MODEL_COSTS: Record<string, { input: number; output: number }> = {
|
||||
"claude-opus-4-20250514": { input: 15, output: 75 },
|
||||
"claude-sonnet-4-20250514": { input: 3, output: 15 },
|
||||
"claude-haiku-3-20250307": { input: 0.25, output: 1.25 },
|
||||
"gpt-5.2": { input: 1.75, output: 14 },
|
||||
"gpt-5.1": { input: 1.25, output: 10 },
|
||||
"gpt-5": { input: 1.25, output: 10 },
|
||||
"gpt-5-mini": { input: 0.25, output: 2 },
|
||||
"gpt-5-nano": { input: 0.05, output: 0.4 },
|
||||
"gpt-4.1": { input: 2, output: 8 },
|
||||
"gpt-4.1-mini": { input: 0.4, output: 1.6 },
|
||||
"gpt-4.1-nano": { input: 0.1, output: 0.4 },
|
||||
"o3": { input: 2, output: 8 },
|
||||
"o3-mini": { input: 1.1, output: 4.4 },
|
||||
"o4-mini": { input: 1.1, output: 4.4 },
|
||||
"o1": { input: 15, output: 60 },
|
||||
"gpt-4o": { input: 2.5, output: 10 },
|
||||
"gpt-4o-mini": { input: 0.15, output: 0.6 },
|
||||
"gpt-4-turbo": { input: 10, output: 30 },
|
||||
"o3-mini": { input: 1.1, output: 4.4 },
|
||||
"gpt-4": { input: 30, output: 60 },
|
||||
"claude-opus-4-6": { input: 5, output: 25 },
|
||||
"claude-opus-4-20250514": { input: 15, output: 75 },
|
||||
"claude-sonnet-4-20250514": { input: 3, output: 15 },
|
||||
"claude-4.5-opus": { input: 5, output: 25 },
|
||||
"claude-4.5-sonnet": { input: 3, output: 15 },
|
||||
"claude-4.5-haiku": { input: 1, output: 5 },
|
||||
"claude-3-5-sonnet": { input: 3, output: 15 },
|
||||
"claude-3-5-haiku": { input: 0.8, output: 4 },
|
||||
"claude-3-opus": { input: 15, output: 75 },
|
||||
"claude-3-haiku": { input: 0.25, output: 1.25 },
|
||||
};
|
||||
|
||||
export function getModelCost(
|
||||
|
||||
@@ -26,17 +26,22 @@ logger = logging.getLogger("agentlens")
|
||||
|
||||
# Cost per 1K tokens (input/output) for common Claude models
|
||||
_MODEL_COSTS: Dict[str, tuple] = {
|
||||
# Claude 3 family
|
||||
"claude-3-opus-20240229": (0.015, 0.075),
|
||||
"claude-3-sonnet-20240229": (0.003, 0.015),
|
||||
"claude-3-haiku-20240307": (0.00025, 0.00125),
|
||||
# Claude 4.5 family
|
||||
"claude-opus-4-6": (0.005, 0.025),
|
||||
"claude-4.5-opus": (0.005, 0.025),
|
||||
"claude-4.5-sonnet": (0.003, 0.015),
|
||||
"claude-4.5-haiku": (0.001, 0.005),
|
||||
# Claude 4 family
|
||||
"claude-sonnet-4-20250514": (0.003, 0.015),
|
||||
"claude-opus-4-20250514": (0.015, 0.075),
|
||||
# Claude 3.5 family
|
||||
"claude-3-5-sonnet-20240620": (0.003, 0.015),
|
||||
"claude-3-5-sonnet-20241022": (0.003, 0.015),
|
||||
"claude-3-5-haiku-20241022": (0.0008, 0.004),
|
||||
# Claude 4 family
|
||||
"claude-sonnet-4-20250514": (0.003, 0.015),
|
||||
"claude-opus-4-20250514": (0.015, 0.075),
|
||||
# Claude 3 family
|
||||
"claude-3-opus-20240229": (0.015, 0.075),
|
||||
"claude-3-sonnet-20240229": (0.003, 0.015),
|
||||
"claude-3-haiku-20240307": (0.00025, 0.00125),
|
||||
# Short aliases for prefix matching
|
||||
"claude-3-opus": (0.015, 0.075),
|
||||
"claude-3-sonnet": (0.003, 0.015),
|
||||
@@ -46,9 +51,9 @@ _MODEL_COSTS: Dict[str, tuple] = {
|
||||
"claude-3.5-sonnet": (0.003, 0.015),
|
||||
"claude-3.5-haiku": (0.0008, 0.004),
|
||||
"claude-sonnet-4": (0.003, 0.015),
|
||||
"claude-opus-4": (0.015, 0.075),
|
||||
"claude-opus-4": (0.005, 0.025),
|
||||
"claude-4-sonnet": (0.003, 0.015),
|
||||
"claude-4-opus": (0.015, 0.075),
|
||||
"claude-4-opus": (0.005, 0.025),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -26,16 +26,34 @@ logger = logging.getLogger("agentlens")
|
||||
|
||||
# Cost per 1K tokens (input/output) for common models
|
||||
_MODEL_COSTS: Dict[str, tuple] = {
|
||||
# GPT-5 family
|
||||
"gpt-5.2": (0.00175, 0.014),
|
||||
"gpt-5.1": (0.00125, 0.01),
|
||||
"gpt-5": (0.00125, 0.01),
|
||||
"gpt-5-mini": (0.00025, 0.002),
|
||||
"gpt-5-nano": (0.00005, 0.0004),
|
||||
# GPT-4.1 family
|
||||
"gpt-4.1": (0.002, 0.008),
|
||||
"gpt-4.1-mini": (0.0004, 0.0016),
|
||||
"gpt-4.1-nano": (0.0001, 0.0004),
|
||||
# o-series reasoning models
|
||||
"o3": (0.002, 0.008),
|
||||
"o3-mini": (0.0011, 0.0044),
|
||||
"o4-mini": (0.0011, 0.0044),
|
||||
"o1": (0.015, 0.06),
|
||||
# GPT-4o family
|
||||
"gpt-4o": (0.0025, 0.01),
|
||||
"gpt-4o-2024-05-13": (0.005, 0.015),
|
||||
"gpt-4o-2024-08-06": (0.0025, 0.01),
|
||||
"gpt-4o-mini": (0.00015, 0.0006),
|
||||
"gpt-4o-mini-2024-07-18": (0.00015, 0.0006),
|
||||
# GPT-4 family
|
||||
"gpt-4": (0.03, 0.06),
|
||||
"gpt-4-32k": (0.06, 0.12),
|
||||
"gpt-4-turbo": (0.01, 0.03),
|
||||
"gpt-4-turbo-2024-04-09": (0.01, 0.03),
|
||||
"gpt-4-turbo-preview": (0.01, 0.03),
|
||||
"gpt-4o": (0.005, 0.015),
|
||||
"gpt-4o-2024-05-13": (0.005, 0.015),
|
||||
"gpt-4o-2024-08-06": (0.0025, 0.01),
|
||||
"gpt-4o-mini": (0.00015, 0.0006),
|
||||
"gpt-4o-mini-2024-07-18": (0.00015, 0.0006),
|
||||
# GPT-3.5 family
|
||||
"gpt-3.5-turbo": (0.0005, 0.0015),
|
||||
"gpt-3.5-turbo-0125": (0.0005, 0.0015),
|
||||
"gpt-3.5-turbo-1106": (0.001, 0.002),
|
||||
|
||||
Reference in New Issue
Block a user