/**
 * LLMProviderPort — BYOM (Bring Your Own Model) abstraction.
 *
 * BYOM axiom (Brain entry 4fa36aad): the SDK ships ZERO default provider.
 * Callers inject the concrete adapter at boot. No model name is hardcoded
 * in this port file or any file that re-exports it.
 *
 * Plan v6 §3.2 — canonical interface.
 *
 * OTel instrumentation: import { createTracedLLMProviderPort } to wrap any
 * LLMProviderPort implementation with OpenTelemetry spans per completion.
 * Gracefully degrades to noop spans when no OTel SDK is installed.
 *
 * @public
 */
// ─── Typed errors ─────────────────────────────────────────────────────────────
/**
 * Base class for all LLM provider errors.
 * Concrete implementations throw subclasses so callers can switch on error type.
 */
export class LLMProviderError extends Error {
    cause;
    /** The provider identifier (e.g. "groq", "litellm", "anthropic"). */
    provider;
    /** The model that was being called (e.g. "llama-3.3-70b"). */
    model;
    constructor(message, provider, model, cause) {
        super(message);
        this.cause = cause;
        this.name = "LLMProviderError";
        this.provider = provider;
        this.model = model;
        Object.setPrototypeOf(this, new.target.prototype);
    }
}
/**
 * Thrown when the LLM provider returns 429 (rate limit exceeded).
 * Callers should retry after the delay indicated in `retryAfterMs`
 * or switch to a fallback provider.
 */
export class LLMRateLimitError extends LLMProviderError {
    /** Retry-After delay in milliseconds. */
    retryAfterMs;
    constructor(message, provider, model, retryAfterMs, cause) {
        super(message, provider, model, cause);
        this.name = "LLMRateLimitError";
        this.retryAfterMs = retryAfterMs;
        Object.setPrototypeOf(this, new.target.prototype);
    }
}
import { SpanStatusCode, trace } from "@opentelemetry/api";
const PORT_TRACER = trace.getTracer("vauban-agent-sdk.ports", "0.1.0");
/**
 * Wrap any LLMProviderPort implementation with OTel spans per complete() call.
 * The span captures the model, provider, and token usage on success.
 * Gracefully degrades to noop spans when no OTel SDK is installed.
 *
 * Usage:
 *   const raw: LLMProviderPort = buildAnthropicAdapter(...);
 *   const traced = createTracedLLMProviderPort(raw);
 *   const resp = await traced.complete(req) // emits "llm-provider.complete" span
 */
export function createTracedLLMProviderPort(impl) {
    return {
        async complete(req) {
            return PORT_TRACER.startActiveSpan("llm-provider.complete", {
                attributes: {
                    "gen_ai.request.model": req.model ?? "unknown",
                    "gen_ai.request.message_count": req.messages.length,
                    "vauban.port.name": "llm-provider",
                },
            }, async (span) => {
                try {
                    const resp = await impl.complete(req);
                    span.setAttributes({
                        "gen_ai.usage.input_tokens": resp.usage.inputTokens,
                        "gen_ai.usage.output_tokens": resp.usage.outputTokens,
                        "gen_ai.response.model": resp.model,
                        "gen_ai.response.finish_reasons": [resp.finishReason],
                    });
                    if (resp.usage.costUsd !== undefined) {
                        span.setAttribute("gen_ai.usage.cost_usd", resp.usage.costUsd);
                    }
                    span.setStatus({ code: SpanStatusCode.OK });
                    return resp;
                }
                catch (err) {
                    const message = err instanceof Error ? err.message : String(err);
                    span.setStatus({ code: SpanStatusCode.ERROR, message });
                    if (err instanceof Error)
                        span.recordException(err);
                    throw err;
                }
                finally {
                    span.end();
                }
            });
        },
        async *stream(req) {
            if (!impl.stream)
                return;
            for await (const chunk of impl.stream(req)) {
                yield chunk;
            }
        },
        estimateCost(req) {
            return impl.estimateCost?.(req) ?? { usd: 0 };
        },
    };
}
//# sourceMappingURL=llm-provider.js.map