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:
Vectry
2026-02-10 03:27:11 +00:00
parent 6bed493275
commit 5256bf005b
17 changed files with 3243 additions and 18 deletions

View 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>
);
}

View 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>
);
}

View 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>
</>
);
}

View 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>
);
}

View 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>
);
}

View 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&apos;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>
);
}

View 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>
);
}

View 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>
);
}

View 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 &quot;false&quot; to disable (default: &quot;true&quot;)</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>
);
}

View 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>
);
}

View 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 &quot;false&quot; 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>
);
}

View 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 &quot;development&quot; 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>
);
}

View 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&lt;string, unknown&gt;</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?: &quot;COMPLETED&quot; | &quot;ERROR&quot;): Promise&lt;void&gt;
</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 &quot;false&quot; to disable</td>
</tr>
</tbody>
</table>
</div>
</section>
</div>
);
}

View File

@@ -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,

View File

@@ -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(

View File

@@ -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),
}

View File

@@ -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),