chore: import upstream snapshot with attribution
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
# integration-opentelemetry/javascript (OpenTelemetry Tracing Example)
|
||||
|
||||
This example demonstrates how to use OpenTelemetry to trace the internal operations of your LLM providers during Promptfoo evaluations.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
npx promptfoo@latest init --example integration-opentelemetry/javascript
|
||||
cd integration-opentelemetry/javascript
|
||||
npm install
|
||||
npx promptfoo@latest eval
|
||||
npx promptfoo@latest view
|
||||
```
|
||||
|
||||
To run the trajectory assertion variant from this directory, use:
|
||||
|
||||
```bash
|
||||
npx promptfoo@latest eval -c promptfooconfig.trajectory.yaml --no-cache
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
This example requires no API keys - it uses a simulated provider that demonstrates tracing patterns.
|
||||
|
||||
## Overview
|
||||
|
||||
Promptfoo's OpenTelemetry integration allows you to:
|
||||
|
||||
- Trace internal operations of your providers without a custom SDK
|
||||
- Use standard OpenTelemetry libraries in any language
|
||||
- Send traces to any OpenTelemetry-compatible backend
|
||||
- Correlate traces with specific test cases and evaluations
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **OTLP receiver starts automatically** - Promptfoo ensures the receiver is ready before evaluations begin
|
||||
2. **Promptfoo generates a trace context** for each test case evaluation
|
||||
3. **The trace context is passed to providers** via the `traceparent` field
|
||||
4. **Providers create child spans** using standard OpenTelemetry SDKs
|
||||
5. **Traces are sent to Promptfoo's OTLP endpoint** (port 4318 by default)
|
||||
6. **Promptfoo correlates traces** with evaluations for analysis
|
||||
|
||||
## Files in This Example
|
||||
|
||||
| File | Description |
|
||||
| --------------------------- | ----------------------------------------------------- |
|
||||
| `promptfooconfig.yaml` | Evaluation config with tracing enabled and assertions |
|
||||
| `provider-simple-traced.js` | Simulated RAG provider with comprehensive tracing |
|
||||
| `trace-assertions.js` | Custom JavaScript assertion for trace validation |
|
||||
| `package.json` | OpenTelemetry dependencies (v2.x API) |
|
||||
|
||||
## Tracing Configuration
|
||||
|
||||
Enable tracing in your `promptfooconfig.yaml`:
|
||||
|
||||
```yaml
|
||||
tracing:
|
||||
enabled: true
|
||||
otlp:
|
||||
http:
|
||||
enabled: true
|
||||
port: 4318
|
||||
host: '0.0.0.0'
|
||||
```
|
||||
|
||||
## Instrumenting Your Provider
|
||||
|
||||
The provider receives trace context from Promptfoo via the `traceparent` field. Here's the pattern used in this example:
|
||||
|
||||
```javascript
|
||||
const { trace, context, SpanStatusCode } = require('@opentelemetry/api');
|
||||
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
|
||||
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
|
||||
const { BatchSpanProcessor } = require('@opentelemetry/sdk-trace-node');
|
||||
const { resourceFromAttributes } = require('@opentelemetry/resources');
|
||||
const { ATTR_SERVICE_NAME } = require('@opentelemetry/semantic-conventions');
|
||||
|
||||
// Initialize OpenTelemetry (v2.x API)
|
||||
const exporter = new OTLPTraceExporter({
|
||||
url: 'http://localhost:4318/v1/traces',
|
||||
});
|
||||
|
||||
const provider = new NodeTracerProvider({
|
||||
resource: resourceFromAttributes({
|
||||
[ATTR_SERVICE_NAME]: 'my-provider',
|
||||
}),
|
||||
spanProcessors: [new BatchSpanProcessor(exporter)],
|
||||
});
|
||||
provider.register();
|
||||
|
||||
const tracer = trace.getTracer('my-provider');
|
||||
|
||||
module.exports = {
|
||||
async callApi(prompt, promptfooContext) {
|
||||
// Parse trace context from Promptfoo
|
||||
if (promptfooContext?.traceparent) {
|
||||
const matches = promptfooContext.traceparent.match(
|
||||
/^(\d{2})-([a-f0-9]{32})-([a-f0-9]{16})-(\d{2})$/,
|
||||
);
|
||||
if (matches) {
|
||||
const [, , traceId, parentId, traceFlags] = matches;
|
||||
|
||||
// Create parent context
|
||||
const parentCtx = trace.setSpanContext(context.active(), {
|
||||
traceId,
|
||||
spanId: parentId,
|
||||
traceFlags: parseInt(traceFlags, 16),
|
||||
isRemote: true,
|
||||
});
|
||||
|
||||
// Run operations within parent context
|
||||
return context.with(parentCtx, async () => {
|
||||
const span = tracer.startSpan('my_operation');
|
||||
try {
|
||||
// Your provider logic here...
|
||||
span.setStatus({ code: SpanStatusCode.OK });
|
||||
return { output: 'result' };
|
||||
} catch (error) {
|
||||
span.recordException(error);
|
||||
span.setStatus({ code: SpanStatusCode.ERROR });
|
||||
throw error;
|
||||
} finally {
|
||||
span.end();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { output: 'result without tracing' };
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Trace-Based Assertions
|
||||
|
||||
This example demonstrates several trace assertion types:
|
||||
|
||||
```yaml
|
||||
assert:
|
||||
# Count spans matching a pattern
|
||||
- type: trace-span-count
|
||||
value:
|
||||
pattern: 'retrieve_document_*'
|
||||
min: 3
|
||||
max: 3
|
||||
|
||||
# Check span duration
|
||||
- type: trace-span-duration
|
||||
value:
|
||||
pattern: 'rag_agent_workflow'
|
||||
max: 5000 # milliseconds
|
||||
|
||||
# Check for error spans
|
||||
- type: trace-error-spans
|
||||
value:
|
||||
max_count: 0
|
||||
```
|
||||
|
||||
The trajectory-specific config at `promptfooconfig.trajectory.yaml` adds:
|
||||
|
||||
- `trajectory:tool-used`
|
||||
- `trajectory:tool-args-match`
|
||||
- `trajectory:tool-sequence`
|
||||
- `trajectory:step-count`
|
||||
|
||||
Promptfoo accepts generic tool span attributes such as `tool.name` and `tool.arguments`, and it also recognizes Vercel AI SDK telemetry attributes such as `ai.toolCall.name`, `ai.toolCall.args`, `ai.toolCall.arguments`, and `ai.toolCall.input`.
|
||||
|
||||
## Viewing Traces
|
||||
|
||||
After running an evaluation, view traces in the web UI:
|
||||
|
||||
```bash
|
||||
npx promptfoo@latest view
|
||||
```
|
||||
|
||||
Click on any test result to see the "Trace Timeline" section showing:
|
||||
|
||||
- Hierarchical span visualization
|
||||
- Duration bars showing relative timing
|
||||
- Status indicators (OK/ERROR)
|
||||
- Span attributes and events
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Configure OpenTelemetry using standard environment variables:
|
||||
|
||||
```bash
|
||||
# Custom endpoint (defaults to Promptfoo's receiver)
|
||||
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
|
||||
|
||||
# Headers for authentication with external collectors
|
||||
export OTEL_EXPORTER_OTLP_HEADERS="api-key=your-key"
|
||||
|
||||
# Enable tracing via environment variable
|
||||
export PROMPTFOO_TRACING_ENABLED=true
|
||||
```
|
||||
|
||||
## Forward to External Collectors
|
||||
|
||||
Send traces to Jaeger, Honeycomb, or other OTLP-compatible backends:
|
||||
|
||||
```yaml
|
||||
tracing:
|
||||
enabled: true
|
||||
forwarding:
|
||||
enabled: true
|
||||
endpoint: 'http://jaeger:4318'
|
||||
headers:
|
||||
'api-key': '${JAEGER_API_KEY}'
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Context Naming Conflicts
|
||||
|
||||
If you see `context.active is not a function`, the OpenTelemetry `context` API conflicts with Promptfoo's context parameter. Rename the parameter:
|
||||
|
||||
```javascript
|
||||
async callApi(prompt, promptfooContext) {
|
||||
// Use promptfooContext for Promptfoo's context
|
||||
// Use context from @opentelemetry/api for tracing
|
||||
}
|
||||
```
|
||||
|
||||
### Traces Not Appearing
|
||||
|
||||
1. Verify `tracing.enabled: true` in config
|
||||
2. Check OTLP receiver is running (look for port 4318 in logs)
|
||||
3. Ensure trace context is properly parsed from `promptfooContext.traceparent`
|
||||
4. Call `spanProcessor.forceFlush()` before returning from provider
|
||||
|
||||
## Dependencies
|
||||
|
||||
This example uses OpenTelemetry v2.x packages:
|
||||
|
||||
| Package | Version | Purpose |
|
||||
| ----------------------------------------- | -------- | ------------------------ |
|
||||
| `@opentelemetry/api` | ^1.9.0 | Core tracing API |
|
||||
| `@opentelemetry/sdk-trace-node` | ^2.0.0 | Node.js tracer provider |
|
||||
| `@opentelemetry/exporter-trace-otlp-http` | ^0.200.0 | OTLP HTTP exporter |
|
||||
| `@opentelemetry/resources` | ^2.0.0 | Resource attributes |
|
||||
| `@opentelemetry/semantic-conventions` | ^1.28.0 | Standard attribute names |
|
||||
Reference in New Issue
Block a user